#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QRegularExpression>
#include <QDateTime>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QValueAxis>
#include <csignal>  // 添加这个头文件
#include <QNetworkInterface>  // 网络接口相关
#include <QHostAddress>       // IP地址处理
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , ds9Process(nullptr)  // 初始化智能指针
{
    ui->setupUi(this);

    // DS9环境检测
    if(!detectDS9()) {
        QMessageBox::critical(this,
                              tr("DS9 Not Found"),
                              tr("SAOImage DS9 is required but not found.\n\n"
                                 "Please install it from:\n"
                                 "http://ds9.si.edu/download/"));

        // 非强制退出，改为禁用相关功能
        ui->checkboxShowFits->setEnabled(false);
        ui->checkboxShowFits->setToolTip(tr("DS9 not installed"));
    }

    initializeUI();
    // 禁用最大化按钮和大小调整
    this->setWindowFlags(this->windowFlags() & ~Qt::WindowMaximizeButtonHint);
    this->setFixedSize(this->size());
    setupPowerControlUI();
    initializeCharts();
    initStatusLeds();
    updateMasterLedStatus(false); // 设置初始指示灯状态
    initializeAlarmThresholds();
    qDebug() << "CCD1 Alarm Thresholds:"
             << ConfigManager::instance().ccd1MinTemp()
             << "-" << ConfigManager::instance().ccd1MaxTemp();
    //从配置文件初始化
    initializeFromConfig();
    // 初始化日志系统
    initializeLogSystem();

    // 初始化网络管理器
    m_networkManager = new QNetworkAccessManager(this);

    // 从配置加载服务器设置
    m_alertServerUrl = ConfigManager::instance().alertServerUrl();
    m_alertServerToken = ConfigManager::instance().alertServerToken();
    m_alertServerTimeout = ConfigManager::instance().alertServerTimeout();

    dataUpdateTimer = new QTimer(this);
    connect(dataUpdateTimer, &QTimer::timeout, this, [this](){
        updateTemperatureData();
        updateTemperatureCurves();    // 更新多曲线
        updateTemperatureDisplay();   // 更新多标签
        updateVacuumData();
        updateCameraStatus();
    });
    dataUpdateTimer->start(1000);

    exposureTimer = new QTimer(this);
    exposureTimer->setSingleShot(true);
    connect(exposureTimer, &QTimer::timeout, this, [this](){
        hwStatus.isExposing = false;
        hwStatus.cameraState = Idle;
        LOG_GENERAL("Exposure completed");
        updateCameraStatus();
    });



    disconnect(ui->btnStartExposure, nullptr, this, nullptr);
    disconnect(ui->btnSetReadoutRate, nullptr, this, nullptr);
    disconnect(ui->btnAbort, nullptr, this, nullptr);
    disconnect(ui->btnBrowsePath, nullptr, this, nullptr);
    disconnect(ui->btnPowerDown, nullptr, this, nullptr);
    disconnect(ui->btnPowerOn, nullptr, this, nullptr);
    disconnect(ui->btnShutterOff, nullptr, this, nullptr);
    disconnect(ui->btnShutterOn, nullptr, this, nullptr);
    disconnect(ui->btnReadIOStatus, nullptr, this, nullptr);

    disconnect(ui->btnmergefits, nullptr, this, nullptr);
    connect(ui->btnmergefits, &QPushButton::clicked, this, &MainWindow::on_btnmergefits_clicked);

    disconnect(ui->btnLinkToCamera, nullptr, this, nullptr);
    connect(ui->btnLinkToCamera, &QPushButton::clicked, this, &MainWindow::onLinkToCameraClicked);

    connect(ui->btnSetReadoutRate, &QPushButton::clicked, this, &MainWindow::on_btnSetReadoutRate_clicked);
    connect(ui->btnStartExposure, &QPushButton::clicked, this, &MainWindow::on_btnStartExposure_clicked);
    connect(ui->btnAbort, &QPushButton::clicked, this, &MainWindow::on_btnAbort_clicked);
    connect(ui->btnBrowsePath, &QPushButton::clicked, this, &MainWindow::on_btnBrowsePath_clicked);
    connect(ui->btnPowerDown, &QPushButton::clicked, this, &MainWindow::on_btnPowerDown_clicked);
    connect(ui->btnPowerOn, &QPushButton::clicked, this, &MainWindow::on_btnPowerOn_clicked);
    connect(ui->btnShutterOff, &QPushButton::clicked, this, &MainWindow::on_btnShutterOff_clicked);
    connect(ui->btnShutterOn, &QPushButton::clicked, this, &MainWindow::on_btnShutterOn_clicked);

    // 在MainWindow构造函数中连接信号槽
    connect(btnCameraPowerOn, &QPushButton::clicked, this, &MainWindow::on_btnCameraPowerOn_clicked);
    connect(btnCameraPowerOff, &QPushButton::clicked, this, &MainWindow::on_btnCameraPowerOff_clicked);
    connect(btnHeatPowerOn, &QPushButton::clicked, this, &MainWindow::on_btnHeatPowerOn_clicked);
    connect(btnHeatPowerOff, &QPushButton::clicked, this, &MainWindow::on_btnHeatPowerOff_clicked);
    connect(btnVaccuPowerOn, &QPushButton::clicked, this, &MainWindow::on_btnVaccuPowerOn_clicked);
    connect(btnVaccuPowerOff, &QPushButton::clicked, this, &MainWindow::on_btnVaccuPowerOff_clicked);
    connect(ui->btnReadIOStatus, &QPushButton::clicked, this, &MainWindow::on_btnReadIOStatus_clicked);
    // 初始化进度条定时器
    progressTimer = new QTimer(this);
    progressTimer->setInterval(200); // 200ms更新一次进度
    connect(progressTimer, &QTimer::timeout, this, [this](){
        updateExposureProgress();
    });

    // 初始化进度条
    ui->ExposureprogressBar->setRange(0, 100);
    ui->ExposureprogressBar->setValue(0);

    // 连接序列完成信号
    connect(this, &MainWindow::sequenceFinished, this, &MainWindow::onSequenceFinished);

    // 初始化序列定时器
    sequenceTimer = new QTimer(this);
    sequenceTimer->setSingleShot(true);

    connect(sequenceTimer, &QTimer::timeout, this, &MainWindow::processSequenceNextFrame);
    disconnect(ui->btnSetFitsPath, nullptr, this, nullptr);
    disconnect(ui->btnStartSequence, nullptr, this, nullptr);
    disconnect(ui->btnStopSequence, nullptr, this, nullptr);
    connect(ui->btnStartSequence, &QPushButton::clicked, this, &MainWindow::on_btnStartSequence_clicked);
    connect(ui->btnStopSequence, &QPushButton::clicked, this, &MainWindow::on_btnStopSequence_clicked);
    // 初始化FITS路径为BIN路径
    fitsSavePath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
    ui->lineEditFitsPath->setText(fitsSavePath);
    // 连接新按钮的信号槽
    connect(ui->btnSetFitsPath, &QPushButton::clicked, this, &MainWindow::on_btnSetFitsPath_clicked);


    disconnect(ui->btnSetTemp_2, nullptr, this, nullptr);
    connect(ui->btnSetTemp_2, &QPushButton::clicked, this, &MainWindow::onSetTemperature);


    // 连接信号槽

    disconnect(ui->sendTempCommandBtn, nullptr, this, nullptr);

    connect(ui->sendTempCommandBtn, &QPushButton::clicked, this, &MainWindow::onSendTempCommand);

    // 连接信号槽

    connect(ui->checkboxShowFits, &QCheckBox::toggled, this, &MainWindow::onCheckboxShowFitsToggled);

    // 在MainWindow构造函数中
    m_alarmCheckTimer = new QTimer(this);
    m_alarmCheckTimer->setInterval(5000); // 每秒检查一次
    connect(m_alarmCheckTimer, &QTimer::timeout, this, &MainWindow::checkAllAlarms);
    m_alarmCheckTimer->start();

    m_heartbeatTimer.setSingleShot(false);
    connect(&m_heartbeatTimer, &QTimer::timeout, [this]() {
        if (!m_isRegistered) {
            LOG_GENERAL("No heartbeat response from MCU received, attempting to re-register....");
            onLinkToCameraClicked(); // 自动重试
        } else {
            // 正常心跳状态下更新最后一次接收时间
            m_lastHeartbeatTime = QDateTime::currentDateTime();
        }

        // 超过30秒无心跳视为断开
        if (m_lastHeartbeatTime.secsTo(QDateTime::currentDateTime()) > 30) {
            m_isRegistered = false;
            LOG_GENERAL("Heartbeat lost; the connection may have been disconnected.");
        }
    });


    ui->loopCountSpinBox->setEnabled(false);
    connect(ui->loopCheckBox, &QCheckBox::stateChanged, this, &MainWindow::onLoopCheckBoxStateChanged);

    // 指示灯相关状态检查定时器 (每500ms检查一次)
    m_statusCheckTimer = new QTimer(this);
    connect(m_statusCheckTimer, &QTimer::timeout, this, &MainWindow::checkCameraConnectionStatus);
    m_statusCheckTimer->start(500);

    // 状态指示灯初始化状态记录
    m_lastStatusTime["master"] = QDateTime::currentMSecsSinceEpoch();
    for (int i = 0; i < 4; ++i) {
        QString slaveId = QString("%1").arg(i);
        m_lastStatusTime[slaveId] = QDateTime::currentMSecsSinceEpoch();
        m_slaveBoardStatus[slaveId] = false;
    }

    // 添加配置按钮到菜单栏
    QMenu *configMenu = menuBar()->addMenu("Configuration");
    QAction *networkConfigAction = configMenu->addAction("Network Settings");
    connect(networkConfigAction, &QAction::triggered, this, &MainWindow::showNetworkConfigDialog);

    // 创建历史任务下拉菜单
    m_historyComboBox = new QComboBox(this);
    m_historyComboBox->setEditable(false);
    m_historyComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    m_historyComboBox->setMinimumWidth(200);

    // 添加标签和下拉菜单到工具栏
    QToolBar* historyToolBar = addToolBar("History");
    historyToolBar->addWidget(new QLabel("拍摄历史:", this));
    historyToolBar->addWidget(m_historyComboBox);

    // 连接信号槽
    connect(m_historyComboBox, QOverload<int>::of(&QComboBox::activated),
            this, &MainWindow::onHistorySelected);

    loadHistoryFromFile();

    m_connectionCheckTimer = new QTimer(this);
    m_connectionCheckTimer->setInterval(5000); // 每秒检查一次
    connect(m_connectionCheckTimer, &QTimer::timeout, this, &MainWindow::checkMCUConnectionStatus);
    m_connectionCheckTimer->start();

    initializeNetwork();
}

MainWindow::~MainWindow()
{   // 停止定时器
    m_logFlushTimer.stop();

    // 关闭所有日志文件
    QMutexLocker locker(&m_logMutex);
    for (auto category : m_logFiles.keys()) {
        // 写入关闭日志
        *m_logStreams[category] << QString("[%1] 程序正常退出\n")
                                       .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));

        m_logStreams[category]->flush();
        m_logFiles[category]->flush();

        delete m_logStreams[category];
        m_logFiles[category]->close();
        delete m_logFiles[category];
    }

    saveHistoryToFile();

    dataUpdateTimer->stop();
    vacuumCheckTimer->stop();
    progressTimer->stop();
    sequenceTimer->stop();
    exposureTimer->stop();
    // m_logTimer.stop();
    m_logFlushTimer.stop();
    //autoUpdateTimer->stop();
    //vacuumUpdateTimer->stop();

    delete dataUpdateTimer;
    delete vacuumCheckTimer;
    delete progressTimer;
    delete sequenceTimer;
    delete exposureTimer;
    //delete autoUpdateTimer;
    //delete vacuumUpdateTimer;

    if (m_autoUpdateLogFile.isOpen()) {
        m_autoUpdateLogFile.close();
    }
    if (m_logFile.isOpen()) {
        LOG_GENERAL("The program exits normally.");
        m_logFile.close();
    }
    delete ui;
}
/*
void MainWindow::initStatusLeds()
{
    // 设置从板卡IP (示例IP，根据实际修改)
    QString slaveIps[4] = {"192.168.1.101", "192.168.1.102",
                           "192.168.1.103", "192.168.1.104"};

    // 在groupBox_11中创建指示灯
    QGridLayout* grid = new QGridLayout(ui->groupBox_11);
    grid->setSpacing(10);

    // 主板卡电源指示灯
    statusLeds.masterPower = new QLabel(this);
    statusLeds.masterPower->setFixedSize(20, 20);
    statusLeds.masterPower->setStyleSheet("background-color: gray; border-radius: 10px;");
    grid->addWidget(new QLabel("主板卡电源"), 0, 0);
    grid->addWidget(statusLeds.masterPower, 0, 1);

    // 初始化4个从板卡指示灯
    for (int i = 0; i < 4; i++) {
        // 设置从板卡IP
        statusLeds.slave[i].ip = slaveIps[i];

        // 创建状态灯
        statusLeds.slave[i].led = new QLabel(this);
        statusLeds.slave[i].led->setFixedSize(20, 20);
        statusLeds.slave[i].led->setStyleSheet("background-color: gray; border-radius: 10px;");

        // 添加到布局
        grid->addWidget(new QLabel(QString("从板卡%1").arg(i, 2, 10, QChar('0'))), i+1, 0);
        grid->addWidget(statusLeds.slave[i].led, i+1, 1);
    }

    // 添加超时检测定时器 (每秒检测一次)
    QTimer* timeoutTimer = new QTimer(this);
    connect(timeoutTimer, &QTimer::timeout, this, &MainWindow::checkSlaveTimeout);
    timeoutTimer->start(1000);
}*/

void MainWindow::showNetworkConfigDialog()
{
    QDialog dialog(this);
    dialog.setWindowTitle("Network Configuration");

    QFormLayout form(&dialog);

    // 主控板卡配置
    QLineEdit *cameraIpEdit = new QLineEdit(ConfigManager::instance().cameraIP());
    QSpinBox *cameraPortSpin = new QSpinBox();
    cameraPortSpin->setRange(1, 65535);
    cameraPortSpin->setValue(ConfigManager::instance().cameraPort());

    // MCU配置
    QLineEdit *mcuIpEdit = new QLineEdit(ConfigManager::instance().mcuIP());
    QSpinBox *mcuPortSpin = new QSpinBox();
    mcuPortSpin->setRange(1, 65535);
    mcuPortSpin->setValue(ConfigManager::instance().mcuPort());

    // ICS服务端口
    QSpinBox *tcpPortSpin = new QSpinBox();
    tcpPortSpin->setRange(1, 65535);
    tcpPortSpin->setValue(ConfigManager::instance().icstcpPort());

    QSpinBox *cameraUdpSpin = new QSpinBox();
    cameraUdpSpin->setRange(1, 65535);
    cameraUdpSpin->setValue(ConfigManager::instance().icsCameraUdpPort());

    QSpinBox *mcuUdpSpin = new QSpinBox();
    mcuUdpSpin->setRange(1, 65535);
    mcuUdpSpin->setValue(ConfigManager::instance().icsMcuUdpPort());

    QSpinBox *slavePortSpin = new QSpinBox();
    slavePortSpin->setRange(1, 65535);
    slavePortSpin->setValue(ConfigManager::instance().slavePort());

    // 添加控件到表单
    form.addRow("Camera IP:", cameraIpEdit);
    form.addRow("Camera Port:", cameraPortSpin);
    form.addRow("MCU IP:", mcuIpEdit);
    form.addRow("MCU Port:", mcuPortSpin);
    form.addRow("ICS TCP Port:", tcpPortSpin);
    form.addRow("ICS Camera UDP Port:", cameraUdpSpin);
    form.addRow("ICS MCU UDP Port:", mcuUdpSpin);
    // 从板卡配置
    QLineEdit *slaveIpEdits[4];
    for(int i = 0; i < 4; ++i) {
        slaveIpEdits[i] = new QLineEdit(ConfigManager::instance().slaveIp(i));
        form.addRow(QString("Slave %1 IP:").arg(i), slaveIpEdits[i]);
    }
    form.addRow("Slave Port:", slavePortSpin);

    QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
                               Qt::Horizontal, &dialog);
    form.addRow(&buttonBox);

    connect(&buttonBox, &QDialogButtonBox::accepted, [&]() {
        // 保存配置
        ConfigManager::instance().setValue("Network", "CameraIP", cameraIpEdit->text());
        ConfigManager::instance().setValue("Network", "CameraPort", cameraPortSpin->value());
        ConfigManager::instance().setValue("Network", "MCUIP", mcuIpEdit->text());
        ConfigManager::instance().setValue("Network", "MCUPort", mcuPortSpin->value());
        ConfigManager::instance().setValue("Network", "ICSTcpPort", tcpPortSpin->value());
        ConfigManager::instance().setValue("Network", "ICSCameraUdpPort", cameraUdpSpin->value());
        ConfigManager::instance().setValue("Network", "ICSMcuUdpPort", mcuUdpSpin->value());

        for(int i = 0; i < 4; ++i) {
            ConfigManager::instance().setValue("Network",
                                               QString("Slave%1IP").arg(i), slaveIpEdits[i]->text());
        }

        ConfigManager::instance().setValue("Network", "SlavePort", slavePortSpin->value());

        QMessageBox::information(this, "Info",
                                 "Network configuration saved. Restart application to apply changes.");
        dialog.accept();
    });

    connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);

    dialog.exec();
}
//记录历史任务
void MainWindow::recordShootingHistory(const QString& fitsName, const QString& fitsPath)
{
    QStringList record;
    record << fitsName
           << fitsPath
           << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

    m_shootingHistory.prepend(record); // 新的记录放在前面

    // 更新下拉菜单 (只保留最近20条记录)
    if(m_shootingHistory.size() > 20) {
        m_shootingHistory.removeLast();
    }

    updateHistoryComboBox();
}

//更新下拉菜单
void MainWindow::updateHistoryComboBox()
{
    m_historyComboBox->clear();

    for (const auto& record : m_shootingHistory) {
        QString displayText = QString("%1 | %2 | %3")
        .arg(record[0])  // 文件名
            .arg(QFileInfo(record[1]).fileName()) // 只显示最后一级路径
            .arg(record[2]); // 日期

        m_historyComboBox->addItem(displayText, QVariant(record[1])); // 存储完整路径作为用户数据
    }
}

void MainWindow::onHistorySelected(int index)
{
    if (index >= 0 && index < m_shootingHistory.size()) {
        QStringList record = m_shootingHistory[index];
        QString filePath = record[1] + "/" + record[0];

        // 在DS9中打开选中的文件
        if (ui->checkboxShowFits->isChecked() && QFile::exists(filePath)) {
            launchDS9(filePath);
        }

        // 在状态栏显示完整信息
        QString info = QString("已选择: %1 | 路径: %2 | 拍摄时间: %3")
                           .arg(record[0])
                           .arg(record[1])
                           .arg(record[2]);
        statusBar()->showMessage(info, 5000);
    }
}

// 保存历史记录到文件
void MainWindow::saveHistoryToFile()
{
    QFile file(QApplication::applicationDirPath() + "/shooting_history.ini");
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream out(&file);
        for (const auto& record : m_shootingHistory) {
            out << record.join("|||") << "\n";
        }
    }
}

// 从文件加载历史记录
void MainWindow::loadHistoryFromFile()
{
    QFile file(QApplication::applicationDirPath() + "/shooting_history.ini");
    if (file.open(QIODevice::ReadOnly)) {
        QTextStream in(&file);
        while (!in.atEnd()) {
            QString line = in.readLine();
            m_shootingHistory.append(line.split("|||"));
        }
        updateHistoryComboBox();
    }
}

void MainWindow::initStatusLeds()
{
    // 设置从板卡IP (示例IP，根据实际修改)
    QString slaveIps[4] = {"192.168.0.101", "192.168.0.102",
                           "192.168.0.103", "192.168.0.104"};
    // 清空原有布局
    QLayout* oldLayout = ui->groupBox_11->layout();
    if(oldLayout) {
        delete oldLayout;
    }

    // 创建水平布局
    QHBoxLayout* hLayout = new QHBoxLayout(ui->groupBox_11);
    hLayout->setSpacing(15);  // 设置控件间距
    hLayout->setContentsMargins(10, 5, 10, 5);  // 设置边距

    // 主板卡电源指示灯
    QWidget* masterWidget = new QWidget();
    QVBoxLayout* masterLayout = new QVBoxLayout(masterWidget);
    masterLayout->setContentsMargins(0, 0, 0, 0);
    masterLayout->setSpacing(3);

    // 主板卡电源指示灯 - 初始红色
    statusLeds.masterPower = new QLabel();
    statusLeds.masterPower->setFixedSize(30, 30);
    statusLeds.masterPower->setStyleSheet("background-color: red; border-radius: 10px;");

    QLabel* masterLabel = new QLabel("相机主控");
    masterLabel->setAlignment(Qt::AlignCenter);

    masterLayout->addWidget(statusLeds.masterPower, 0, Qt::AlignHCenter);
    masterLayout->addWidget(masterLabel);
    hLayout->addWidget(masterWidget);

    // 4个从板卡指示灯
    for (int i = 0; i < 4; i++) {
        QWidget* slaveWidget = new QWidget();
        QVBoxLayout* slaveLayout = new QVBoxLayout(slaveWidget);
        slaveLayout->setContentsMargins(0, 0, 0, 0);
        slaveLayout->setSpacing(3);

        statusLeds.slave[i].led = new QLabel();
        statusLeds.slave[i].led->setFixedSize(24, 24);
        statusLeds.slave[i].led->setStyleSheet("background-color: red; border-radius: 12px;");

        QLabel* slaveLabel = new QLabel(QString("CCD%1").arg(i));
        slaveLabel->setAlignment(Qt::AlignCenter);

        slaveLayout->addWidget(statusLeds.slave[i].led, 0, Qt::AlignHCenter);
        slaveLayout->addWidget(slaveLabel);
        hLayout->addWidget(slaveWidget);
    }

    // 添加伸缩因子使布局居中
    hLayout->insertStretch(0, 1);
    hLayout->addStretch(1);

    // 添加超时检测定时器 (每秒检测一次)
    QTimer* timeoutTimer = new QTimer(this);
    connect(timeoutTimer, &QTimer::timeout, this, &MainWindow::checkSlaveTimeout);
    timeoutTimer->start(1000);
}

void MainWindow::querySlaveStatus()
{
    // 查询每个从板卡状态
    for (int i = 0; i < 4; i++) {
        QString cmd = QString("link_to_camera:$ip=%1$port=11008")
        .arg(m_localIp);
        sendCommandToSlave(cmd, "192.168.0.101");

        //cmd = QString("ccd_status:ID=%1").arg(i, 2, 10, QChar('0'));
        //sendCommandToSlave(cmd, statusLeds.slave[i].ip);
    }
}



void MainWindow::processSlaveStatus(const QString &response)
{
    // 提取板卡ID（00-03）
    QString idStr = response.mid(14, 1); // 从"ccd_status:ID=00"中提取"00"
    bool ok;
    int id = idStr.toInt(&ok);

    // 验证ID有效性
    if (ok && id >= 0 && id <= 3) {
        // 更新最后响应时间（收到任何响应都视为上电）
        statusLeds.slave[id].lastResponseTime = QDateTime::currentMSecsSinceEpoch();

        // 设置指示灯为绿色（上电状态）
        statusLeds.slave[id].led->setStyleSheet(
            "background-color: green; border-radius: 12px;");

        LOG_GENERAL(QString("Confirmation of power-on from Slave %1").arg(id, 2, 10, QChar('0')));
    }
}

void MainWindow::checkSlaveTimeout()
{
    qint64 currentTime = QDateTime::currentMSecsSinceEpoch();

    for (int i = 0; i < 4; i++) {
        // 超过5秒未收到任何响应视为下电
        if (currentTime - statusLeds.slave[i].lastResponseTime > 5000) {
            statusLeds.slave[i].led->setStyleSheet(
                "background-color: gray; border-radius: 12px;");

            // 可选：记录超时日志（调试用）
            static qint64 lastLogTime[4] = {0};
            if (currentTime - lastLogTime[i] > 10000) { // 每10秒记录一次超时
                LOG_GENERAL(QString("Slave %1 Response timeout").arg(i, 2, 10, QChar('0')));
                lastLogTime[i] = currentTime;
            }
        }
    }
}

// 在 sendCommandToSlave 方法中使用配置的从板卡端口
void MainWindow::sendCommandToSlave(const QString& cmd, const QString& ip)
{
    static QUdpSocket slaveSocket;
    quint16 port = ConfigManager::instance().slavePort(); // 使用配置的端口

    QByteArray data = cmd.toUtf8();
    slaveSocket.writeDatagram(data, QHostAddress(ip), port);

    LOG_CAMERA(QString("sent to %1: %2").arg(ip).arg(cmd));
}

void MainWindow::updateMasterLedStatus(bool isConnected)
{
    QString style;

    if (!isConnected) {
        // 未连接状态 - 红色
        style = "background-color: red; border-radius: 10px;";
    }
    else {
        // 根据相机状态设置颜色
        switch(cameraState.status) {
        case 0:  // 空闲
            style = "background-color: green; border-radius: 10px;";
            break;
        case 1:  // 曝光中
            style = "background-color: blue; border-radius: 10px;";
            break;
        case 2:  // 读出中
            style = "background-color: cyan; border-radius: 10px;";
            break;
        //case -1: //
            //style = "background-color: red; border-radius: 10px;";
            //break;
        default:
            style = "background-color: yellow; border-radius: 10px;";
        }
    }

    statusLeds.masterPower->setStyleSheet(style);
}

void MainWindow::updateCameraControls(bool isExposing) {
    // 曝光按钮
    ui->btnStartExposure->setEnabled(!isExposing);

    // 读出速度控制
    ui->comboBoxReadoutRate->setEnabled(!isExposing);

    // 快门控制按钮组
    ui->btnShutterOn->setEnabled(!isExposing);
    ui->btnShutterOff->setEnabled(!isExposing);


}

void MainWindow::initializeFromConfig()
{
    // Network
    cameraIp = ConfigManager::instance().cameraIP();
    cameraPort = ConfigManager::instance().cameraPort();
    mcuIp = ConfigManager::instance().mcuIP();
    mcuPort = ConfigManager::instance().mcuPort();
    serverPort = ConfigManager::instance().icstcpPort();

    // 初始化从板卡IP
    for (int i = 0; i < 4; ++i) {
        statusLeds.slave[i].ip = ConfigManager::instance().slaveIp(i);
    }

    // Paths
    binPath = ConfigManager::instance().binPath();
    fitsSavePath = ConfigManager::instance().fitsPath();
    m_logDirectory = ConfigManager::instance().logPath();

    // Temperature
    ui->spinTemperature_2->setValue(ConfigManager::instance().ccdTargetTemp());


    // Update UI
    ui->lineEditCameraIp->setText(cameraIp);
    ui->spinBoxCameraPort->setValue(cameraPort);
    ui->lineEditMcuIp->setText(mcuIp);
    ui->spinBoxMcuPort->setValue(mcuPort);
    ui->lineEditBinPath->setText(binPath);
    ui->lineEditBinPath_2->setText(m_logDirectory);
    ui->lineEditFitsPath->setText(fitsSavePath);
}

// 实现报警推送方法
void MainWindow::checkAllAlarms()
{
    QDateTime now = QDateTime::currentDateTime();
    auto alarms = alarmStatus.allAlarms();

    LOG_ALARM("Starting alarm check...");

    for (auto it = alarms.begin(); it != alarms.end(); ++it) {
        QString sensorName = it.key();
        double currentValue = getCurrentValue(sensorName);
        LOG_ALARM(QString("Checking %1: Current=%2, Min=%3, Max=%4")
                      .arg(sensorName)
                      .arg(currentValue)
                      .arg(it.value()->minThreshold)
                      .arg(it.value()->maxThreshold));

        if (it.value()->checkAlarm(currentValue, now)) {
            LOG_ALARM(QString("Alarm triggered for %1! Current value %2 is out of range [%3,%4]")
                          .arg(sensorName)
                          .arg(currentValue)
                          .arg(it.value()->minThreshold)
                          .arg(it.value()->maxThreshold));
            sendAlarmToServer(sensorName);
        }
    }

    checkAlarmRecovery();
    //alarmStatus.ccd1.isAlarming = true;
    updateAlarmUI();

    LOG_ALARM("Alarm check completed");
}

void MainWindow::sendAlarmToServer(const QString& alarmName)
{
    QNetworkRequest request;
    request.setUrl(QUrl(ConfigManager::instance().alertServerUrl()));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    QJsonObject alarmData;
    alarmData["status"] = 0; // 异常状态
    alarmData["module"] = getModuleCode(alarmName);
    alarmData["reason"] = getAlarmReason(alarmName);
    alarmData["value"] = alarmStatus.allAlarms()[alarmName]->currentValue;
    alarmData["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);

    QNetworkReply* reply = m_networkManager->post(
        request, QJsonDocument(alarmData).toJson());

    connect(reply, &QNetworkReply::finished, [reply]() {
        if (reply->error() != QNetworkReply::NoError) {
            qDebug() << "Alarm POST failed:" << reply->errorString();
        }
        reply->deleteLater();
    });
}

int MainWindow::getModuleCode(const QString& alarmName)
{
    static QMap<QString, int> moduleMap = {
        {"Vacuum", 1},
        {"Current", 2},
        {"CCD1", 4}, {"CCD2", 4}, // ...所有温度传感器
        {"Other", 8}
    };
    return moduleMap.value(alarmName, 8);
}

QString MainWindow::getAlarmReason(const QString& alarmName)
{
    auto alarm = alarmStatus.allAlarms()[alarmName];
    QString direction = alarm->currentValue < alarm->minThreshold ? "低于" : "高于";
    return QString("%1 %2阈值 (当前: %3, 范围: [%4, %5])")
        .arg(alarmName)
        .arg(direction)
        .arg(alarm->currentValue)
        .arg(alarm->minThreshold)
        .arg(alarm->maxThreshold);
}

void MainWindow::checkAlarmRecovery()
{
    QDateTime now = QDateTime::currentDateTime();
    auto alarms = alarmStatus.allAlarms();

    for (auto it = alarms.begin(); it != alarms.end(); ++it) {
        auto alarm = it.value();
        double currentValue = getCurrentValue(it.key());

        // 检查是否恢复正常范围
        bool isInRange = (currentValue >= alarm->minThreshold &&
                          currentValue <= alarm->maxThreshold);

        if (alarm->isAlarming && isInRange) {
            // 恢复正常状态
            alarm->isAlarming = false;
            alarm->thresholdExceeded = false;
            sendRecoveryToServer(alarm->alarmName);

            // 可选：记录恢复日志
            LOG_ALARM(QString("%1 恢复正常 (当前值: %2)")
                          .arg(alarm->alarmName)
                          .arg(currentValue));
        }
    }
}

void MainWindow::sendRecoveryToServer(const QString& alarmName)
{
    QJsonObject recoveryData;
    recoveryData["status"] = 1; // 正常状态
    recoveryData["module"] = getModuleCode(alarmName);
    recoveryData["reason"] = QString("%1 恢复正常范围").arg(alarmName);
    recoveryData["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);

    // ...发送POST请求（同sendAlarmToServer）
}

void MainWindow::logAlarms() {
    auto now = QDateTime::currentDateTime();
    auto alarms = alarmStatus.allAlarms();

    for (auto it = alarms.begin(); it != alarms.end(); ++it) {
        if (it.value()->isAlarming) {
            QString duration = QString::number(it.value()->alarmStartTime.secsTo(now));
            LOG_ALARM(QString("%1 Alarm confirmed, exceeding the threshold for %2 seconds")
                          .arg(it.key())
                          .arg(duration));
        }
        else if (it.value()->thresholdExceeded) {
            QString duration = QString::number(it.value()->alarmStartTime.secsTo(now));
            LOG_ALARM(QString("%1Warning - Exceeding the threshold has lasted %2 seconds")
                          .arg(it.key())
                          .arg(duration));
        }
    }
}

void MainWindow::initializeAlarmThresholds()
{
    // 温度报警
    alarmStatus.ccd1.alarmName = "CCD1";
    alarmStatus.ccd1.minThreshold = ConfigManager::instance().ccd1MinTemp();
    alarmStatus.ccd1.maxThreshold = ConfigManager::instance().ccd1MaxTemp();
    alarmStatus.ccd2.alarmName = "CCD2";
    alarmStatus.ccd2.minThreshold = ConfigManager::instance().ccd2MinTemp();
    alarmStatus.ccd2.maxThreshold = ConfigManager::instance().ccd2MaxTemp();
    alarmStatus.ccd3.alarmName = "CCD3";
    alarmStatus.ccd3.minThreshold = ConfigManager::instance().ccd3MinTemp();
    alarmStatus.ccd3.maxThreshold = ConfigManager::instance().ccd3MaxTemp();
    alarmStatus.ccd4.alarmName = "CCD4";
    alarmStatus.ccd4.minThreshold = ConfigManager::instance().ccd4MinTemp();
    alarmStatus.ccd4.maxThreshold = ConfigManager::instance().ccd4MaxTemp();

    alarmStatus.base1.alarmName = "BASE1";
    alarmStatus.base1.minThreshold = ConfigManager::instance().base1MinTemp();
    alarmStatus.base1.maxThreshold = ConfigManager::instance().base1MaxTemp();
    alarmStatus.base2.alarmName = "BASE2";
    alarmStatus.base2.minThreshold = ConfigManager::instance().base2MinTemp();
    alarmStatus.base2.maxThreshold = ConfigManager::instance().base2MaxTemp();

    alarmStatus.absorbCold.alarmName = "absorbCold";
    alarmStatus.absorbCold.minThreshold = ConfigManager::instance().absorbColdMinTemp();
    alarmStatus.absorbCold.maxThreshold = ConfigManager::instance().absorbColdMaxTemp();

    alarmStatus.circuit.alarmName = "circuit";
    alarmStatus.circuit.minThreshold = ConfigManager::instance().circuitMinTemp();
    alarmStatus.circuit.maxThreshold = ConfigManager::instance().circuitMaxTemp();

    alarmStatus.dewar.alarmName = "dewar";
    alarmStatus.dewar.minThreshold = ConfigManager::instance().dewarMinTemp();
    alarmStatus.dewar.maxThreshold = ConfigManager::instance().dewarMaxTemp();

    alarmStatus.backup1.alarmName = "backup1";
    alarmStatus.backup1.minThreshold = ConfigManager::instance().backup1MinTemp();
    alarmStatus.backup1.maxThreshold = ConfigManager::instance().backup1MaxTemp();

    alarmStatus.backup2.alarmName = "backup2";
    alarmStatus.backup2.minThreshold = ConfigManager::instance().backup2MinTemp();
    alarmStatus.backup2.maxThreshold = ConfigManager::instance().backup2MaxTemp();

    alarmStatus.output.alarmName = "output";
    alarmStatus.output.minThreshold = ConfigManager::instance().outputMin();
    alarmStatus.output.maxThreshold = ConfigManager::instance().outputMax();

    // 真空报警
    alarmStatus.vacuum.alarmName = "Vacuum";
    alarmStatus.vacuum.minThreshold = ConfigManager::instance().vacuumMin();
    alarmStatus.vacuum.maxThreshold = ConfigManager::instance().vacuumMax();

    // 电流报警
    alarmStatus.current.alarmName = "Current";
    alarmStatus.current.minThreshold = ConfigManager::instance().currentMin();
    alarmStatus.current.maxThreshold = ConfigManager::instance().currentMax();

    LOG_ALARM(QString("CCD1 Thresholds: Min=%1, Max=%2").arg(alarmStatus.ccd1.minThreshold).arg(alarmStatus.ccd1.maxThreshold));
    LOG_ALARM(QString("CCD2 Thresholds: Min=%1, Max=%2").arg(alarmStatus.ccd2.minThreshold).arg(alarmStatus.ccd2.maxThreshold));
    LOG_ALARM(QString("CCD3 Thresholds: Min=%1, Max=%2").arg(alarmStatus.ccd3.minThreshold).arg(alarmStatus.ccd3.maxThreshold));
    LOG_ALARM(QString("CCD4 Thresholds: Min=%1, Max=%2").arg(alarmStatus.ccd4.minThreshold).arg(alarmStatus.ccd4.maxThreshold));
    LOG_ALARM(QString("BASE1 Thresholds: Min=%1, Max=%2").arg(alarmStatus.base1.minThreshold).arg(alarmStatus.base1.maxThreshold));
    LOG_ALARM(QString("BASE2 Thresholds: Min=%1, Max=%2").arg(alarmStatus.base2.minThreshold).arg(alarmStatus.base2.maxThreshold));
    LOG_ALARM(QString("Vacuum Thresholds: Min=%1, Max=%2").arg(alarmStatus.vacuum.minThreshold).arg(alarmStatus.vacuum.maxThreshold));
    //LOG_ALARM(QString("CCD1 Thresholds: Min=%1, Max=%2").arg(alarmStatus.ccd1.minThreshold).arg(alarmStatus.ccd1.maxThreshold));
}
/*
void MainWindow::updateAlarmUI()
{
    qDebug() << "Updating alarm UI...";
    auto alarms = alarmStatus.allAlarms();

    for (auto it = alarms.begin(); it != alarms.end(); ++it) {
        QLabel* label = findChild<QLabel*>("label" + it.key());
        if (label) {
            if (it.value()->isAlarming) {
                label->setStyleSheet("color: red; font-weight: bold;");
            } else if (it.value()->thresholdExceeded) {
                label->setStyleSheet("color: orange;");
            } else {
                label->setStyleSheet("");
            }
        }
    }

    // 全局报警状态灯
    bool anyAlarm = std::any_of(alarms.begin(), alarms.end(),
                                [](const auto& alarm) { return alarm->isAlarming; });

    ui->labelGlobalAlarm->setText(anyAlarm ? "ALARM" : "NORMAL");
    ui->labelGlobalAlarm->setStyleSheet(
        QString("background-color: %1; color: white;").arg(anyAlarm ? "red" : "green"));
}*/
void MainWindow::updateAlarmUI() {
    auto alarms = alarmStatus.allAlarms();
    bool anyAlarm = false;

    for (auto it = alarms.begin(); it != alarms.end(); ++it) {
        if (it.value()->isAlarming) {
            anyAlarm = true;
            break;  // 发现一个报警即可终止循环
        }
    }

    LOG_ALARM(QString("Global alarm update - AnyAlarm:%1").arg(anyAlarm));
    ui->labelGlobalAlarm->setText(anyAlarm ? "ALARM" : "NORMAL");
    ui->labelGlobalAlarm->setStyleSheet(
        QString("background-color: %1; color: white;").arg(anyAlarm ? "red" : "green"));
}
double MainWindow::getCurrentValue(const QString& sensorName) const
{
    double value = 0.0;
    // 根据传感器名称返回对应的当前值
    if (sensorName == "CCD1") {
        value =  hwStatus.tempData.ccd1;
    } else if (sensorName == "CCD2") {
        value = hwStatus.tempData.ccd2;
    } else if (sensorName == "CCD3") {
        value = hwStatus.tempData.ccd3;
    } else if (sensorName == "CCD4") {
        value = hwStatus.tempData.ccd4;
    } else if (sensorName == "Base1") {
        value = hwStatus.tempData.base1;
    } else if (sensorName == "Base2") {
        value = hwStatus.tempData.base2;
    } else if (sensorName == "AbsorbCold") {
        value = hwStatus.tempData.absorbCold;
    } else if (sensorName == "Circuit") {
        value = hwStatus.tempData.circuit;
    }else if (sensorName == "Dewar") {
        value = hwStatus.tempData.dewar;
    } else if (sensorName == "Backup1") {
        value = hwStatus.tempData.backup1;
    } else if (sensorName == "Backup2") {
        value = hwStatus.tempData.backup2;
    } else if (sensorName == "Output") {
        value = hwStatus.tempData.outputPower;
    }else if (sensorName == "Vacuum") {
        value = hwStatus.currentVacuum;
    }
    //else if (sensorName == "Current") {
        //return hwStatus.currentCurrent; // 假设您有一个currentCurrent成员变量
    //}
    //LOG_TEMP_VAC(QString("VALUE = %1").arg(value));
    qWarning() << "Unknown sensor name:" << sensorName;
    return value;
}

/*
void MainWindow::updateCurrentData(double current)
{
    hwStatus.currentCurrent = current;
    ui->labelCurrent->setText(QString("%1 A").arg(current, 0, 'f', 2));
}*/
/*0801xiugai
void MainWindow::initializeNetwork()
{
    tcpServer = new QTcpServer(this);
    if (!tcpServer->listen(QHostAddress::Any, 9076)) {
        LOG_GENERAL("TCP Server failed to start: " + tcpServer->errorString());
    } else {
        connect(tcpServer, &QTcpServer::newConnection, this, &MainWindow::handleNewConnection);
        LOG_GENERAL(QString("TCP Server started on port %1").arg(serverPort));
    }

    //初始化温控、真空度UDP监听

    mcuSocket = new QUdpSocket(this);
    if (!mcuSocket->bind(QHostAddress::Any, 11000)) {
        LOG_GENERAL("UDP Socket failed to bind: " + mcuSocket->errorString());
    } else {
        connect(mcuSocket, &QUdpSocket::readyRead, this, &MainWindow::processMCUData);
        LOG_GENERAL("mcu UDP Socket bound to port 11000");
    }
    LOG_GENERAL("mcuIp:   " + mcuIp);
    LOG_GENERAL("mcuPort:   " + mcuPort);
    //mcuIp = "10.201.11.191";
    //mcuPort = 10009;
    //mcuIp = "192.168.0.200";
    //mcuPort = 10009;

    // 获取本地IP（排除回环地址）
    foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol && !address.isLoopback()) {
            m_localIp = address.toString();
            break;
        }
    }

    // Link按钮连接
    connect(ui->btnLinkToCamera, &QPushButton::clicked, this, &MainWindow::onLinkToCameraClicked);
    // 初始化相机UDP监听
    cameraSocket = new QUdpSocket(this);
    if (!cameraSocket->bind(QHostAddress::Any, 11008)) {
        LOG_GENERAL("camera Socket failed to bind: " + cameraSocket->errorString());
    } else {
        connect(cameraSocket, &QUdpSocket::readyRead, this, &MainWindow::processCameraResponse);
        LOG_GENERAL("camera UDP Socket bound to port 11008");
    }
    // 加载默认设置
    //cameraIp = "10.201.11.191";
    // cameraPort = 10001;
    //cameraIp = "192.168.0.100";
    //cameraPort = 10001;

    LOG_GENERAL("cameraIp:   " + cameraIp);

    //binPath = QCoreApplication::applicationDirPath() + "/data";
    //QDir().mkpath(binPath);  // 创建目录
    //binPath = "/home/csst/PS_mosaic/data/bin_tmp";
    LOG_GENERAL("binPath:   " + binPath);
}*/

// 在 initializeNetwork() 方法中替换硬编码的端口
void MainWindow::initializeNetwork()
{
    // 使用配置的TCP端口
    serverPort = ConfigManager::instance().icstcpPort();
    tcpServer = new QTcpServer(this);

    if (!tcpServer->listen(QHostAddress::Any, serverPort)) {
        LOG_GENERAL("TCP Server failed to start: " + tcpServer->errorString());
    } else {
        connect(tcpServer, &QTcpServer::newConnection, this, &MainWindow::handleNewConnection);
        LOG_GENERAL(QString("TCP Server started on port %1").arg(serverPort));
    }

    // 使用配置的UDP端口
    mcuSocket = new QUdpSocket(this);
    if (!mcuSocket->bind(QHostAddress::Any, ConfigManager::instance().icsMcuUdpPort())) {
        LOG_GENERAL("MCU UDP Socket failed to bind: " + mcuSocket->errorString());
    } else {
        connect(mcuSocket, &QUdpSocket::readyRead, this, &MainWindow::processMCUData);
        LOG_GENERAL(QString("MCU UDP Socket bound to port %1").arg(ConfigManager::instance().icsMcuUdpPort()));
    }

    // 相机UDP监听使用配置的端口
    cameraSocket = new QUdpSocket(this);
    if (!cameraSocket->bind(QHostAddress::Any, ConfigManager::instance().icsCameraUdpPort())) {
        LOG_GENERAL("Camera UDP Socket failed to bind: " + cameraSocket->errorString());
    } else {
        connect(cameraSocket, &QUdpSocket::readyRead, this, &MainWindow::processCameraResponse);
        LOG_GENERAL(QString("Camera UDP Socket bound to port %1").arg(ConfigManager::instance().icsCameraUdpPort()));
    }

    // 更新从板卡IP配置
    for (int i = 0; i < 4; ++i) {
        statusLeds.slave[i].ip = ConfigManager::instance().slaveIp(i);
    }

    // 获取本地IP（排除回环地址）
    foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol && !address.isLoopback()) {
            m_localIp = address.toString();
            break;
        }
    }
}

// Link按钮点击处理
void MainWindow::onLinkToCameraClicked() {
    QString cmd1 = QString("link_to_camera:$ip=192.168.0.233$port=11000");
    QString cmd2 = QString("link_to_camera:$ip=192.168.0.233$port=%2").arg(11008);

    sendCommandToMCU(cmd1);
    LOG_CAMERA("Device registration information has been sent: " + cmd1);
    sendCameraCommand(cmd2);
    //querySlaveStatus();
    QString cmd3 = QString("link_to_camera:$ip=192.168.0.233$port=11008");
    sendCommandToSlave(cmd3, "192.168.0.101");
    sendCommandToSlave(cmd3, "192.168.0.102");
    sendCommandToSlave(cmd3, "192.168.0.103");
    sendCommandToSlave(cmd3, "192.168.0.104");
}

//配置保存修改
/*void MainWindow::on_btnSetCameraIp_clicked()
{
    QString newIp = ui->lineEditCameraIp->text();
    ConfigManager::instance().setValue("Network", "CameraIP", newIp);
    cameraIp = newIp;
}*/

// Similar for other settings...
// 更新心跳状态显示
void MainWindow::updateHeartbeatStatus() {
    QString status;
    QColor color;

    if (m_isRegistered) {
        int elapsed = m_lastHeartbeatTime.secsTo(QDateTime::currentDateTime());
        status = QString("已连接 (%1秒前)").arg(elapsed);
        color = elapsed < 10 ? Qt::green : Qt::yellow;
    } else {
        status = "未连接";
        color = Qt::red;
    }

    ui->labelHeartbeatStatus->setText(status);
    ui->labelHeartbeatStatus->setStyleSheet(QString("color: %1").arg(color.name()));
}

void MainWindow::on_btnsetmcu_clicked(){
    mcuIp = ui->lineEditMcuIp->text();
    mcuPort = ui->spinBoxMcuPort->value();
    LOG_GENERAL("mcuIp:   " + mcuIp);
    //LOG_GENERAL("mcuPort:   " + std::to_string(mcuPort));
}
void MainWindow::on_btnsetcamera_clicked(){
    cameraIp = ui->lineEditCameraIp->text();
    cameraPort = ui->spinBoxCameraPort->value();
    LOG_CAMERA("cameraIp:   " + cameraIp);
    //LOG_GENERAL("cameraPort:   " + std::to_string(cameraPort));
}

void MainWindow::sendCameraCommand(const QString &cmd) {
    if (!cameraSocket || !cameraSocket->isValid()) {
        LOG_CAMERA("Camera UDP Socket not ready");
        return;
    }

    QByteArray data = cmd.toUtf8();
    cameraSocket->writeDatagram(data, QHostAddress(cameraIp), cameraPort);
    LOG_CAMERA("sent command to camera: " + cmd);
}

//新增配置界面的手动输入通信方法
void MainWindow::onSendCameraCommand()
{
    QString cmd = ui->cameraCommandEdit->text();
    LOG_CAMERA("向相机发送指令: " + cmd);
    if(!cmd.isEmpty()){
        sendCameraCommand(cmd);
    }
}
void MainWindow::sendCommandToMCU(const QString &cmd, bool isAutoUpdate)
{
    if (!mcuSocket || !mcuSocket->isValid()) {
        if (!isAutoUpdate) {
            LOG_GENERAL("MCU UDP Socket not ready");
        }
        return;
    }

    QByteArray data = cmd.toUtf8();
    mcuSocket->writeDatagram(data, QHostAddress(mcuIp), mcuPort);

    if (isAutoUpdate) {
        //writeAutoUpdateLog("发送自动更新命令: " + cmd);
    } else {
        //LOG_GENERAL("向温控单片机发送指令: " + cmd);
    }
}

void MainWindow::onSendTempCommand()
{
    QString cmd = ui->tempCommandEdit->text();
    if(!cmd.isEmpty()){
        sendCommandToMCU(cmd);
    }
}

void MainWindow::on_btnBrowsePath_clicked() {
    QString path = QFileDialog::getExistingDirectory(
        this,
        "选择BIN文件存储路径",
        QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)
        );

    if (!path.isEmpty()) {
        binPath = path;
        ui->lineEditBinPath->setText(path);
        LOG_GENERAL("The storage path of BIN has been set to: " + path);
    }
}

void MainWindow::onLoopCheckBoxStateChanged(int state)
{
    // 启用/禁用循环次数选择框
    ui->loopCountSpinBox->setEnabled(state == Qt::Checked);
}
/*
void MainWindow::on_btnStartExposure_clicked() {
    static qint64 lastSendTime = 0;
    qint64 now = QDateTime::currentMSecsSinceEpoch();
    if (now - lastSendTime < 500) return;
    lastSendTime = now;

    if (cameraState.status != 0) {
        LOG_CAMERA("相机当前状态为[%1]，无法开始曝光");
        return;
    }

    // 重置状态
    m_currentExpInfo = ExposureInfo();
    m_currentExpInfo.isManual = true;
    m_currentExpInfo.tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    m_currentExpInfo.imageName = ui->lineEditBaseFilename->text();

    if (m_currentExpInfo.imageName.isEmpty()) {
        m_currentExpInfo.imageName = "manual_" + m_currentExpInfo.tag;
    }

    // 收集手动拍摄的头信息
    m_manualKeywords = collectManualHeaderInfo();

    // 如果是本地手动拍摄，自动递增IMG_NUM
    if (!isSequenceRunning) { // 非序列拍摄时
        m_localImageCounter++;
        ui->lineEditImgNum->setText(QString::number(m_localImageCounter));
    }

    // 设置进度条参数
    currentExposureTime = ui->doubleSpinBoxExposureTime->value();
    currentProgressTotalTime = currentExposureTime + 12;
    progressStartTime = QDateTime::currentMSecsSinceEpoch();

    ui->ExposureprogressBar->setRange(0, 100);
    ui->ExposureprogressBar->setValue(0);
    progressTimer->start();

    // 生成唯一标识
    QString tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    cameraState.currentTag = tag;

    // 获取参数
    // 获取当前选择的帧类型
    QString frameType = ui->comBoxframetype->currentData().toString();
    int darkMode = (frameType == "bs" || frameType == "dk") ? 1 : 0;
    int pinning = ui->checkBoxPinning->isChecked() ? 1 : 0;
    int dump = ui->checkBoxDump->isChecked() ? 1 : 0;

    // 设置基础文件名
    currentBaseFilename = ui->lineEditBaseFilename->text();
    if (currentBaseFilename.isEmpty()) {
        currentBaseFilename = "manual_" + tag;
    }
    LOG_CAMERA("currentBaseFilename: " + currentBaseFilename);

    // 构建指令
    QString cmd = QString("get_frame_290:$tag=%1$dark=%2$pinning=%3$dump=%4$exp=%5")
                      .arg(tag).arg(darkMode).arg(pinning).arg(dump).arg(currentExposureTime);

    sendCameraCommand(cmd);
    LOG_CAMERA("已发送指令 " + cmd);
    LOG_CAMERA("已启动曝光，任务ID: " + tag);
}*/

void MainWindow::on_btnStartExposure_clicked()
{
    static qint64 lastSendTime = 0;
    qint64 now = QDateTime::currentMSecsSinceEpoch();
    if (now - lastSendTime < 500) return;
    lastSendTime = now;

    if (cameraState.status != 0) {
        //LOG_CAMERA("The current status of the camera is [%1]，can not StartExposure");
        //return;
    }
    // 重置状态
    m_currentExpInfo = ExposureInfo();
    m_currentExpInfo.isManual = true;
    m_currentExpInfo.tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    m_currentExpInfo.imageName = ui->lineEditBaseFilename->text();

    if (m_currentExpInfo.imageName.isEmpty()) {
        m_currentExpInfo.imageName = "manual_" + m_currentExpInfo.tag;
    }
    // 获取当前选择的帧类型
    QString frameType = ui->comBoxframetype->currentData().toString();
    int darkMode = (frameType == "bs" || frameType == "dk") ? 1 : 0;

    // 保存到当前曝光信息中
    m_currentExpInfo.darkMode = darkMode;
    m_currentExpInfo.frameType = frameType;

    // 获取当前读出速度
    int rateIndex = ui->comboBoxReadoutRate->currentData().toInt();
    QString readoutRate = readoutRateMap.value(rateIndex, "Unknown");

    // 保存到当前曝光信息中
    m_currentExpInfo.readoutRate = readoutRate;

    // 收集手动拍摄的头信息
    m_manualKeywords = collectManualHeaderInfo();

    // 如果是本地手动拍摄，自动递增IMG_NUM
    if (!isSequenceRunning) { // 非序列拍摄时
        m_localImageCounter++;
        ui->lineEditImgNum->setText(QString::number(m_localImageCounter));
    }

    static int currentLoop = 0;
    static int totalLoops = 1;
    static ExposureInfo loopExposureInfo;

    // 如果是第一次启动或新循环
    if(currentLoop == 0 && ui->loopCheckBox->isChecked()) {
        totalLoops = ui->loopCheckBox->isChecked() ? ui->loopCountSpinBox->value() : 1;

        // 保存当前拍摄参数
        loopExposureInfo = m_currentExpInfo;
        loopExposureInfo.isManual = true;
        loopExposureInfo.tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");

        // 更新进度标签
        ui->labelProgressStatus->setText(
            QString("Loop %1/%2").arg(currentLoop+1).arg(totalLoops));
    }


    // 设置基础文件名
    currentBaseFilename = ui->lineEditBaseFilename->text();
    if (currentBaseFilename.isEmpty()) {
        currentBaseFilename = "manual_" + m_currentExpInfo.tag;
    }
    LOG_CAMERA("currentBaseFilename: " + currentBaseFilename);
    if(ui->loopCheckBox->isChecked()){
        m_currentExpInfo.imageName = QString("%1_loop%2")
        .arg(currentBaseFilename)
            .arg(currentLoop+1, 2, 10, QChar('0'));
    }
    // 设置进度条参数
    //currentProgressTotalTime = currentExposureTime + 12;

    // 初始化进度控制
    currentPhase = PHASE_EXPOSING;
    progressStartTime = QDateTime::currentMSecsSinceEpoch();
    currentExposureTime = ui->doubleSpinBoxExposureTime->value();

    // 设置进度条范围
    ui->ExposureprogressBar->setRange(0, 100);
    ui->ExposureprogressBar->setValue(0);

    // 启动定时器
    progressTimer->start(200); // 200ms更新一次

    // 生成唯一标识
    //QString tag = QString("%1_%2").arg(loopExposureInfo.tag).arg(currentLoop+1, 2, 10, QChar('0'));
    // cameraState.currentTag = tag;
    // 生成唯一标识
    QString tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    cameraState.currentTag = tag;

    // 构建指令
    //int darkMode = (ui->comBoxframetype->currentData().toString() == "bs" ||
    //ui->comBoxframetype->currentData().toString() == "dk") ? 1 : 0;
    int pinning = ui->checkBoxPinning->isChecked() ? 1 : 0;
    int dump = ui->checkBoxDump->isChecked() ? 1 : 0;

    QString cmd = QString("get_frame_290:$tag=%1$dark=%2$pinning=%3$dump=%4$exp=%5")
                      .arg(tag).arg(darkMode).arg(pinning).arg(dump).arg(currentExposureTime);

    sendCameraCommand(cmd);
    if(ui->loopCheckBox->isChecked()){
        LOG_CAMERA(QString("Start Loop Exp [%1/%2]").arg(currentLoop+1).arg(totalLoops));
    }

    // 更新循环计数器
    currentLoop++;
    if(currentLoop >= totalLoops && ui->loopCheckBox->isChecked()) {
        currentLoop = 0;
        LOG_CAMERA("Loop Exp completed");
    }
}

void MainWindow::resetImageCounter()
{
    m_localImageCounter = 0;
    ui->lineEditImgNum->setText("0");
}
void MainWindow::updateExposureProgress() {
    if (progressStartTime == 0) return;

    qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - progressStartTime;
    int elapsedSecs = elapsed / 1000;
    int progress = 0;
    QString status;

    // 根据当前阶段计算进度
    if (currentPhase == PHASE_EXPOSING) {
        // 曝光阶段 (0-50%)
        progress = qMin(50, static_cast<int>(elapsedSecs * 50 / (currentExposureTime+0.1)));
        status = QString("EXPOSING %1/%2 Seconds").arg(elapsedSecs).arg(currentExposureTime);

        // 检查是否应该转入读出阶段
        if (elapsedSecs >= (currentExposureTime+0.1)) {
            currentPhase = PHASE_READOUT;
            progressStartTime = QDateTime::currentMSecsSinceEpoch(); // 重置计时
            //LOG_GENERAL("进入读出阶段");
        }
    }
    else if (currentPhase == PHASE_READOUT) {
        // 读出阶段 (50-100%)
        int readoutElapsed = elapsedSecs;
        progress = 50 + qMin(50, readoutElapsed * 50 / 10); // 固定10秒读出
        status = QString("READOUT %1/10 Seconds").arg(readoutElapsed);

        // 检查是否完成
        if (readoutElapsed >= 10) {
            currentPhase = PHASE_IDLE;
            progressTimer->stop();
            progress = 100;
            status = "完成";
            //LOG_GENERAL("曝光流程完成");
        }
    }

    // 更新UI
    ui->ExposureprogressBar->setValue(progress);
    ui->labelProgressStatus->setText(status);

    // 可视化区分阶段
    QString style;
    if (currentPhase == PHASE_EXPOSING) {
        style = "QProgressBar::chunk { background: #5B9BD5; }"; // 蓝色表示曝光
    } else if (currentPhase == PHASE_READOUT) {
        style = "QProgressBar::chunk { background: #70AD47; }"; // 绿色表示读出
    }
    ui->ExposureprogressBar->setStyleSheet(style);
}/*
void MainWindow::updateExposureProgress()
{

    if (progressStartTime == 0) return;

    qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - progressStartTime;
    int elapsedSecs = elapsed / 1000;

    // 计算进度百分比
    int progress = qMin(100, static_cast<int>(elapsed * 100 / (currentProgressTotalTime * 1000)));

    // 更新进度条
    ui->ExposureprogressBar->setValue(progress);

    // 更新标签显示
    QString status;
    if (elapsedSecs < currentExposureTime) {
        status = QString("曝光中 %1/%2秒").arg(elapsedSecs).arg(currentExposureTime);
    } else {
        status = QString("转换中 %1/10秒").arg(elapsedSecs - currentExposureTime);
    }

    if (isSequenceRunning) {
        status += QString(" | 任务 %1/%2").arg(currentSequenceIndex + 1).arg(sequenceTasks.size());
    }
    // 循环状态
    if(ui->loopCheckBox->isChecked()) {
        static int currentLoop = 1;
        static int totalLoops = ui->loopCountSpinBox->value();
        status += QString(" | 循环 %1/%2").arg(currentLoop).arg(totalLoops);
    }
    ui->labelProgressStatus->setText(status);

}*/
/*
void MainWindow::onSetLogDirClicked()
{
    QString dir = QFileDialog::getExistingDirectory(this, "选择日志目录", m_logDirectory);
    if (!dir.isEmpty()) {
        m_logDirectory = dir;
        QDir().mkpath(m_logDirectory);

        // 重新打开日志文件
        if (m_logFile.isOpen()) {
            m_logFile.close();
        }

        QString logFileName = QDateTime::currentDateTime().toString("yyyyMMdd") + ".log";
        m_logFile.setFileName(m_logDirectory + "/" + logFileName);
        QString autoupdatelogFileName = QDateTime::currentDateTime().toString("yyyyMMdd") + ".autoupdatelog";
        m_autoUpdateLogFile.setFileName(m_logDirectory + "/" + autoupdatelogFileName);

        if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
            qWarning() << "无法打开日志文件:" << m_logFile.fileName();
            return;
        }
        if (!m_autoUpdateLogFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
            qWarning() << "无法打开自动更新日志文件:" << m_autoUpdateLogPath;
            return;
        }

        LOG_GENERAL("日志目录已更改为: " + m_logDirectory);
    }
}*/

void MainWindow::on_btnSetReadoutRate_clicked() {
    // 1. 获取用户选择的档位
    int rateIndex = ui->comboBoxReadoutRate->currentData().toInt();

    // 2. 验证档位有效性
    if (!readoutRateMap.contains(rateIndex)) {
        LOG_CAMERA("Invalid read out rate gear: " + QString::number(rateIndex));
        QMessageBox::warning(this, "error", "Invalid read out rate gear set");
        return;
    }
    // 3. 检查相机状态
    if (!isCameraIdle()) {
        LOG_CAMERA("The camera is busy and cannot set the readout speed.");
        QMessageBox::warning(this, "warning", "The camera is currently busy, please wait for it to be idle.");
        return;
    }
    // 4. 构建并发送指令
    QString cmd = QString("readout_rate_290:$rate=%1").arg(rateIndex);
    sendCameraCommand(cmd);
    sendCommandToSlave(cmd, "192.168.0.101");
    sendCommandToSlave(cmd, "192.168.0.102");
    sendCommandToSlave(cmd, "192.168.0.103");
    sendCommandToSlave(cmd, "192.168.0.104");
    LOG_CAMERA("Readout rate setting command has been sent: " + cmd);
}

void MainWindow::handleReadoutRateResponse(const QString &response) {
    // 解析响应格式: readout_rate_290=$info=ok$rate=0/1/2/3
    QStringList parts = response.split('$');
    QString info, rate;
    bool ok;
    int strNum = 100;
    for (const QString &part : parts) {
        if (part.startsWith("info=")) {
            info = part.mid(5);
        } else if (part.startsWith("rate=")) {
            rate = part.mid(5);
            int strNum = rate.toInt(&ok);
        }
    }

    int rateIndex = ui->comboBoxReadoutRate->currentData().toInt();
    if (ok && info == "ok" && strNum == rateIndex && m_successCount == 5) {
        LOG_CAMERA("Readout rate set successfully: " + rate);
        // 更新UI显示
        ui->labelCurrentReadoutRate->setText("current rate: " + rate.toUpper());
        QMessageBox::information(this, "successfully", "readout rate has been set to: " + rate);
        m_successCount = 0;
    } else if (info == "busy") {
        LOG_CAMERA("Camera is busy, Failed to set");
        QMessageBox::warning(this, "Failed", "Camera is busy, Failed to set");
        m_successCount = 0;
    } else if (info == "ok") {
        m_successCount++; // 成功计数增加
    }
    else {
        LOG_CAMERA("未知响应: " + response);
    }
}

void MainWindow::on_btnAbort_clicked() {
    if (cameraState.status == 0) {
       LOG_CAMERA("Warning: The camera is currently idle; no need to abort.");
       return;
    }

    sendCameraCommand("abort_290");
    LOG_CAMERA("Abort command has been sent.");
    //hwStatus.cameraState = Idle;
    currentPhase = PHASE_IDLE;
    progressTimer->stop();
    ui->ExposureprogressBar->setValue(0);
    ui->labelProgressStatus->setText("Aborted");

    // 重置样式
    ui->ExposureprogressBar->setStyleSheet("");

    updateCameraControls(false);

    // 重置循环状态
    static int currentLoop = 0;
    currentLoop = 0;

}
void MainWindow::on_btnPowerDown_clicked()  {
    //LOG_GENERAL("相机下电函数被调用");
    sendCameraCommand("camera_pwr_off");
    LOG_CAMERA("Camera power-off command has been sent.");
}
void MainWindow::on_btnPowerOn_clicked()  {
    //LOG_GENERAL("相机上电函数被调用");
    sendCameraCommand("camera_pwr_on");
    LOG_CAMERA("Camera power-on command has been sent.");
}
void MainWindow::on_btnShutterOff_clicked()  {
    sendCameraCommand("io_set_shutter:0");
    LOG_CAMERA("Shutter off command has been sent");
}
void MainWindow::on_btnShutterOn_clicked()  {
    sendCameraCommand("io_set_shutter:1");
    LOG_CAMERA("Shutter on command has been sent");
}
void MainWindow::on_btnCameraPowerOff_clicked()  {
    sendCommandToMCU("io_camera_off");
    modifyCameraStatusFile(false);  // false表示设置为0
    LOG_CAMERA("IO Camera power-off command has been sent.");
}
void MainWindow::on_btnCameraPowerOn_clicked()  {
    sendCommandToMCU("io_camera_on");
    modifyCameraStatusFile(false);  // false表示设置为0
    LOG_CAMERA(" IO Camera power-oN command has been sent.");
}
void MainWindow::on_btnHeatPowerOff_clicked()  {
    sendCommandToMCU("io_heating_off");
    LOG_GENERAL("Heating power-off command has been sent.");
}
void MainWindow::on_btnHeatPowerOn_clicked()  {
    sendCommandToMCU("io_heating_on");
    LOG_GENERAL("Heating power-on command has been sent");
}
void MainWindow::on_btnVaccuPowerOff_clicked()  {
    sendCommandToMCU("io_vaccu_off");
    LOG_GENERAL("Vaccu power-off command has been sent");
}
void MainWindow::on_btnVaccuPowerOn_clicked()  {
    sendCommandToMCU("io_vaccu_on");
    LOG_GENERAL("Vaccu power-on command has been sent");
}
void MainWindow::on_btnReadIOStatus_clicked()  {
    sendCommandToMCU("io_status_read");
    LOG_GENERAL("已发送io_status_read指令");
}

void MainWindow::modifyCameraStatusFile(bool powerOn)
{
    QString filePath = "/home/shenxiuqi/Desktop/camera.status";  // camera.status路径
    QFile file(filePath);

    if (!file.exists()) {
        qDebug() << "Error: File not found";
        return;
    }

    // 读取文件内容
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Error: Cannot open file for reading";
        return;
    }

    QStringList lines;
    QTextStream in(&file);
    while (!in.atEnd()) {
        QString line = in.readLine();
        if (line.startsWith("power_status=")) {
            line = QString("power_status=%1").arg(powerOn ? "1" : "0");
        }
        lines.append(line);
    }
    file.close();

    // 写入修改后的内容
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qDebug() << "Error: Cannot open file for writing";
        return;
    }

    QTextStream out(&file);
    foreach (const QString &line, lines) {
        out << line << "\n";
    }
    file.close();

    qDebug() << "Power status updated to:" << (powerOn ? "1" : "0");
}
void MainWindow::on_btnSetFitsPath_clicked()
{
    QString path = QFileDialog::getExistingDirectory(
        this,
        "Choose The FITS saving path",
        fitsSavePath.isEmpty() ?
            QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) :
            fitsSavePath
        );

    if (!path.isEmpty()) {
        fitsSavePath = path;
        ui->lineEditFitsPath->setText(fitsSavePath);
        LOG_GENERAL("The FITS saving path has been set to: " + fitsSavePath);
    }
}

void MainWindow::onCheckboxShowFitsToggled(bool checked)
{
    //ui->lineEditFitsFile->setEnabled(checked);
    //ui->pushBtnFitsFile->setEnabled(checked);

    // 取消勾选时关闭已打开的DS9窗口
    if(!checked && !ds9Process.isNull()) {
        ds9Process->terminate();
    }
}
/*
void MainWindow::onBrowseFitsFileClicked()
{
    QString fitsFilter = "FITS Images (*.fits *.fit *.fts);;All Files (*)";
    QString filePath = QFileDialog::getOpenFileName(this,
                                                    "Select FITS File",
                                                    QDir::homePath(),
                                                    fitsFilter);

    if(!filePath.isEmpty()) {
        //ui->lineEditFitsFile->setText(filePath);
        launchDS9(filePath);
    }
}*/

void MainWindow::launchDS9(const QString& fitsPath)
{
    // 结束已有DS9进程
    if(!ds9Process.isNull()) {
        ds9Process->kill();
    }

    ds9Process.reset(new QProcess(this));

    // 设置DS9启动参数
    QStringList args;
    args << "-fits" << fitsPath
         << "-cmap" << "heat"
         << "-scale" << "log"
         << "-zoom" << "to fit";

    // 进程结束处理
    connect(ds9Process.data(), QOverload<int>::of(&QProcess::finished),
            [this](int exitCode) {
                qDebug() << "DS9 exited with code:" << exitCode;
            });

    // 启动DS9
    ds9Process->start(ds9Path, args);

    // 错误处理
    if(!ds9Process->waitForStarted(1000)) {
        QMessageBox::warning(this,
                             "DS9 Launch Failed",
                             "Could not start DS9. Please ensure it's installed at " + ds9Path);
    }
}

bool MainWindow::detectDS9()
{
    // 检查标准路径
    if(QFile::exists(ds9Path)) {
        ui->labelDs9Path->setText(ds9Path);
        return true;
    }
    // 检查标准路径
    const QStringList ds9Paths = {
        "/usr/bin/ds9",                      // Linux标准路径
        "/usr/local/bin/ds9",                // 手动安装路径
        "C:/Program Files/SAOImageDS9/ds9.exe",  // Windows路径
        QDir::homePath() + "/Applications/SAOImageDS9.app/Contents/MacOS/ds9" // macOS
    };

    // 自动检测可用路径
    for (const auto &path : ds9Paths) {
        if (QFile::exists(path)) {
            ds9Path = path;
            qInfo() << "DS9 detected at:" << path;
            ui->labelDs9Path->setText(ds9Path);
            return true;
        }
    }

    // 尝试通过系统命令查找
    QProcess which;
#ifdef Q_OS_WIN
    which.start("where", {"ds9.exe"});
#else
    which.start("which", {"ds9"});
#endif
    which.waitForFinished(1000);

    if (which.exitCode() == 0) {
        ds9Path = which.readAllStandardOutput().trimmed();
        ui->labelDs9Path->setText(ds9Path);
        return true;
    }

    return false;
}

void MainWindow::processCameraResponse() {
    qint64 elapsedSec = m_startTime.secsTo(QDateTime::currentDateTime());
    while (cameraSocket->hasPendingDatagrams()) {
        //if (elapsedSec % 5 == 0) {
        //LOG_GENERAL("相机响应处理循环运行正常");
        //
        QByteArray datagram;
        datagram.resize(cameraSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        cameraSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
        QString response = QString::fromUtf8(datagram).trimmed();
        //LOG_CAMERA("Received a response: " + response);

        // 分类处理响应
        if (response.startsWith("camera_status:")) {
            // 更新主板状态时间戳
            m_lastStatusTime["master"] = QDateTime::currentMSecsSinceEpoch();
            handleStatusResponse(response);
        }
        else if (response.startsWith("get_frame_290:$tag=")) {
            LOG_CAMERA("Received a header info response " + response);
            processCameraHeader(response);
        }
        else if (response.startsWith("get_frame_290:done=")) {
            LOG_CAMERA("Received a get frame done response " + response);
            handleFrameDone(response);
            LOG_CAMERA(QString("==> start new process for: %1").arg(response));
        }
        else if (response.startsWith("readout_rate_290=$")) {
            LOG_CAMERA("Received ReadoutRate response: " + response);
            handleReadoutRateResponse(response);
        }
        else if (response.startsWith("ccd_status:")) {
            // 提取从板ID (格式: ccd_status:ID=00$ready=1)
            QString idStr = response.mid(response.indexOf("ID=")+3, 1);
            if (m_lastStatusTime.contains(idStr)) {
                m_lastStatusTime[idStr] = QDateTime::currentMSecsSinceEpoch();
                m_slaveBoardStatus[idStr] = (response.contains("ready=1"));
            }
            processSlaveStatus(response);
        }
        else {
            //LOG_CAMERA("收到响应: " + response);
        }
    }
}

void MainWindow::checkCameraConnectionStatus()
{
    qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
    const qint64 timeout = 1500; // 1.5秒超时

    // 检查主板状态
    bool masterConnected = (currentTime - m_lastStatusTime["master"] <= timeout);
    updateMasterLedStatus(masterConnected);

    // 检查从板状态
    for (int i = 0; i < 4; ++i) {
        QString slaveId = QString("%1").arg(i);
        bool slaveConnected = (currentTime - m_lastStatusTime[slaveId] <= timeout) &&
                              m_slaveBoardStatus[slaveId];

        // 更新从板指示灯
        QString style = slaveConnected ?
                            "background-color: green; border-radius: 12px;" :
                            "background-color: red; border-radius: 12px;";

        statusLeds.slave[i].led->setStyleSheet(style);
    }
}

void MainWindow::processCameraHeader(const QString& response)
{

    QMap<QString, QString> params;
    QStringList parts = response.split('$');

    for (const QString& part : parts) {
        int eqPos = part.indexOf('=');
        if (eqPos > 0) {
            QString key = part.left(eqPos);
            QString value = part.mid(eqPos+1);
            params[key] = value;
        }
    }

    // 更新当前曝光信息
    m_currentExpInfo.tag = params.value("tag");
    m_currentExpInfo.expStart = parseCameraTimestamp(params.value("exp_start"));
    m_currentExpInfo.expEnd = parseCameraTimestamp(params.value("exp_end"));
    m_currentExpInfo.readoutStart = parseCameraTimestamp(params.value("readout_start"));

    // 验证时间戳有效性
    if (!m_currentExpInfo.expStart.isValid() ||
        !m_currentExpInfo.expEnd.isValid() ||
        !m_currentExpInfo.readoutStart.isValid()) {
        LOG_CAMERA("Parsed an invalid timestamp");
        m_currentExpInfo = ExposureInfo(); // 重置为无效状态
    }
}

void MainWindow::handleStatusResponse(const QString &response)
{

    //camera_status:ID=z1$ready=-1$shutter=0$rate=1
    QStringList parts = response.mid(response.indexOf(':')+1).split('$');
    foreach (const QString &part, parts) {
        QStringList kv = part.split('=');
        if (kv.size() == 2) {
            if (kv[0] == "ID") {
                cameraState.id = kv[1];
            } else if (kv[0] == "ready") {
                cameraState.status = kv[1].toInt();
                updateMasterLedStatus(true); // 状态变化时立即更新指示灯
            } else if (kv[0] == "shutter") {
                cameraState.shutter = kv[1].toInt();
            } else if (kv[0] == "rate") {
                cameraState.rate = kv[1];
            }
        }
        if (kv[0] == "status") {
            cameraState.status = kv[1].toInt();
            updateMasterLedStatus(true); // 状态变化时立即更新指示灯
        }
    }

    // 更新UI显示
    QString statusText;
    switch (cameraState.status) {
    case -1: statusText = "未上电"; break;
    case 0: statusText = "空闲"; break;
    case 1: statusText = "曝光中"; break;
    case 2: statusText = "读出中"; break;
    }
    ui->labelCameraStatus->setText(QString("状态: %1 | 快门: %2 | 读出: %3")
                                       .arg(statusText)
                                       .arg(cameraState.shutter ? "开" : "关")
                                       .arg(cameraState.rate));
    // 根据新状态更新控件
    bool isExposing = (cameraState.status != 0);
    updateCameraControls(isExposing);

}



QDateTime MainWindow::parseCameraTimestamp(const QString& timestampStr)
{
    // 格式: YYMMDDhhmmssSSS (年(2位)月日时分秒毫秒)
    if (timestampStr.length() != 15) {
        LOG_CAMERA("Invalid timestamp format: " + timestampStr);
        return QDateTime();
    }

    int year = 2000 + timestampStr.mid(0, 2).toInt(); // 假设21世纪
    int month = timestampStr.mid(2, 2).toInt();
    int day = timestampStr.mid(4, 2).toInt();
    int hour = timestampStr.mid(6, 2).toInt();
    int minute = timestampStr.mid(8, 2).toInt();
    int second = timestampStr.mid(10, 2).toInt();
    int msec = timestampStr.mid(12, 3).toInt();

    return QDateTime(QDate(year, month, day),
                     QTime(hour, minute, second, msec));
}

void MainWindow::on_btnbin2fits_clicked(){
    //binPath = "/home/csst/PS_mosaic/data/bin_tmp";
    QString binFile = "/home/shenxiuqi/Documents/debug.bin";
    QString fitsFile = "debug_00.fits";

    // 使用C++转换
    if (convertBinToFits(binFile, "/home/shenxiuqi/Documents/debug", "cpp")) {
        LOG_GENERAL("转换成功: " + fitsFile);
    } else {
        LOG_GENERAL("转换失败: " + binFile);
    }
}

bool MainWindow::convertBinToFits(const QString& binPath,
                                  const QString& fitsPath,
                                  const QString& baseFilename,
                                  const std::vector<std::pair<std::string, std::string>>& keywords)
{
    // 准备关键字列表（包含原有keywords和时间戳）
    std::vector<std::pair<std::string, std::string>> allKeywords = keywords;

    // 如果当前曝光信息匹配，添加时间戳关键字
    if (!m_currentExpInfo.tag.isEmpty() && m_currentExpInfo.tag == baseFilename.right(11)) {
        // 计算曝光时间（秒）
        double expTime = m_currentExpInfo.expStart.msecsTo(m_currentExpInfo.expEnd) / 1000.0;

        // 添加时间戳关键字
        allKeywords.emplace_back("EXPSTART", m_currentExpInfo.expStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("EXPEND", m_currentExpInfo.expEnd.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("READOUTS", m_currentExpInfo.readoutStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        allKeywords.emplace_back("EXPTIME", QString::number(expTime, 'f', 3).toStdString());

        LOG_CAMERA(QString("Add to FITS Header: EXPSTART=%1, EXPEND=%2, READOUTS=%3, EXPTIME=%4")
                       .arg(m_currentExpInfo.expStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.expEnd.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.readoutStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(expTime));
    }

    // 原有的转换逻辑（使用包含时间戳的allKeywords）
    try {
        bool result = FitsConverter::processBinToFits(
            binPath.toStdString(),
            fitsPath.toStdString(),
            baseFilename.toStdString(),
            allKeywords  // 传入所有关键字（包含时间戳）
            );

        if (!result) {
            LOG_CAMERA(QString("BIN2FITS fialed: %1").arg(binPath));
            return false;
        }

        QString fitsFile = fitsPath + "/" + baseFilename + ".fits";
        if (!QFile::exists(fitsFile)) {
            LOG_CAMERA(QString("The FITS file has not been generated: %1").arg(fitsFile));
            return false;
        }

        LOG_CAMERA(QString("BIN2FITS successfully: %1 -> %2").arg(binPath).arg(fitsFile));
        return true;
    } catch (const std::exception& e) {
        LOG_CAMERA(QString("An exception occurred during the conversion process: %1").arg(e.what()));
        return false;
    }
}

void MainWindow::handleFrameDone(const QString &response)
{
    //hwStatus.cameraState = MainWindow::Idle;
    QStringList parts = response.split(':');
    QString tag;

    foreach (const QString &part, parts) {
        if (part.startsWith("done=")) {
            tag = part.mid(5);
            break;
        }
    }

    if (tag.isEmpty()) {
        LOG_CAMERA("Invalid task ID");
        return;
    }

    // 记录收到的tag
    m_receivedTags.insert(tag);

    // 检查是否收到所有4个tag（00,01,02,03）
    if (m_receivedTags.size() == 4) {
        // 准备FITS头信息
        std::vector<std::pair<std::string, std::string>> keywords = collectManualHeaderInfo();

    // 如果当前曝光信息匹配，添加时间戳关键字
        /*if (m_currentExpInfo.isValid()) {
        // 计算曝光时间（秒）
            double expTime = m_currentExpInfo.expStart.msecsTo(m_currentExpInfo.expEnd) / 1000.0;

        // 添加时间戳关键字
            keywords.emplace_back("EXPSTART", m_currentExpInfo.expStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
            keywords.emplace_back("EXPEND", m_currentExpInfo.expEnd.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
            keywords.emplace_back("READOUTS", m_currentExpInfo.readoutStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
            keywords.emplace_back("EXPTIME", QString::number(expTime, 'f', 3).toStdString());

            LOG_CAMERA(QString("ADD TO FITS HEADER: EXPSTART=%1, EXPEND=%2, READOUTS=%3, EXPTIME=%4")
                       .arg(m_currentExpInfo.expStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.expEnd.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.readoutStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(expTime));
        }*/

        // 使用QtConcurrent并行处理4个bin文件
        QList<QFuture<bool>> futures;
        QStringList fitsFilePaths;

        for (const QString &receivedTag : m_receivedTags) {
            QString binFile = QString("%1/%2.bin").arg(binPath).arg(receivedTag);
            QString fitsFile = fitsSavePath + "/" + currentBaseFilename + "_" + receivedTag + ".fits";
            fitsFilePaths.append(fitsFile);

            futures.append(QtConcurrent::run([this, binFile, receivedTag, keywords]() {
                return convertBinToFits(binFile, fitsSavePath,
                                        currentBaseFilename + "_" + receivedTag,
                                        keywords);
            }));
        }

        // 等待所有转换完成
        for (auto &future : futures) {
            future.waitForFinished();
        }

        // 检查所有FITS文件是否生成成功
        bool allFilesExist = true;
        for (const QString &fitsFile : fitsFilePaths) {
            if (!QFile::exists(fitsFile)) {
                allFilesExist = false;
                LOG_CAMERA(QString("The FITS file has not been generated: %1").arg(fitsFile));
                break;
            }
        }

        // 如果所有FITS文件都存在，则合并它们
        if (allFilesExist) {
            QString mergedFitsPath = fitsSavePath + "/" + currentBaseFilename;
            if(!m_currentExpInfo.isManual && m_pendingResponse.isValid()){
                auto& pending = m_pendingResponse;
                QString doneResponse = QString(
                                           "<ID=302><CamID=%1><TIME=%2><SHUTSTA=closed>"
                                           "<EXPST_TIME=%3><CUR_TEM=%4><INFO=OK><ACTION=DONE>"
                                           "<SENDTIME=%5><FITS_Name=%6>;")
                                           .arg(pending.camId)
                                           .arg(pending.expTime, 0, 'f', 2)
                                           .arg(pending.expStartTimeStr)
                                           .arg(hwStatus.currentTemp, 0, 'f', 2)
                                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
                                           .arg(pending.imageName);

                sendResponse(doneResponse, pending.client);
                LOG_CAMERA(QString("Remote Exp completed. [Cam:%1][Tag:%2]").arg(pending.camId).arg(tag));
            }
            if (mergeFitsFiles(fitsFilePaths, mergedFitsPath)) {
                LOG_CAMERA(QString("mergeFitsFiles successfully: %1").arg(mergedFitsPath));
                recordShootingHistory(mergedFitsPath, fitsSavePath); // 记录历史

                // 可选：删除原始单个FITS文件
                for (const QString &fitsFile : fitsFilePaths) {
                    QFile::remove(fitsFile);
                }
            } else {
                LOG_CAMERA("mergeFitsFiles failed");
            }
        }
        QString mergedFitsPath1 = fitsSavePath + "/" + currentBaseFilename;
        QMetaObject::invokeMethod(this, [this, mergedFitsPath1]() {
            if (ui->checkboxShowFits->isChecked()) {
                launchDS9(mergedFitsPath1);
            }
        }, Qt::QueuedConnection);

        m_currentExpInfo = ExposureInfo();

        cameraState.status = 0;
        // 清空已接收的tag
        m_receivedTags.clear();
        PendingResponse pendingResponse;
        m_pendingResponse = pendingResponse;

        // 如果是序列拍摄模式，则继续下一个任务
        if (isSequenceRunning) {
            QTimer::singleShot(1000, this, &MainWindow::processSequenceNextFrame);
        }
        // 如果是循环模式且未完成，启动下一次拍摄
        if (ui->loopCheckBox->isChecked()){
            static int currentLoop = 0;
            static int totalLoops = 1;

            if(currentLoop > 0 && currentLoop < totalLoops) {
                 QTimer::singleShot(1000, this, &MainWindow::on_btnStartExposure_clicked);
            } else {
                currentLoop = 0;
                ui->labelProgressStatus->setText("准备就绪");
            }
        }
    }
}

// handleFrameDone函数:支持python转换方式
/*
void MainWindow::handleFrameDone(const QString &response)
{
    QStringList parts = response.split(':');
    QString tag;

    foreach (const QString &part, parts) {
        if (part.startsWith("done=")) {
            tag = part.mid(5);
            break;
        }
    }

    if (tag.isEmpty()) {
        LOG_CAMERA(" Invalid task ID");
        return;
    }
    binPath = "/home/csst/PS_mosaic/data/bin_tmp";
    QString binFile = QString("%1/%2.bin").arg(binPath).arg(tag);
    //QString fitsFile = QString("%1/%2.fits").arg(fitsSavePath).arg(tag);

    std::vector<std::pair<std::string, std::string>> keywords = collectManualHeaderInfo();

    // 如果当前曝光信息匹配，添加时间戳关键字
    if (m_currentExpInfo.isValid()) {
        // 计算曝光时间（秒）
        double expTime = m_currentExpInfo.expStart.msecsTo(m_currentExpInfo.expEnd) / 1000.0;

        // 添加时间戳关键字
        keywords.emplace_back("EXPSTART", m_currentExpInfo.expStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        keywords.emplace_back("EXPEND", m_currentExpInfo.expEnd.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        keywords.emplace_back("READOUTS", m_currentExpInfo.readoutStart.toString("yyyy-MM-ddThh:mm:ss.zzz").toStdString());
        keywords.emplace_back("EXPTIME", QString::number(expTime, 'f', 3).toStdString());

        LOG_CAMERA(QString("ADD TO FITS HEADER: EXPSTART=%1, EXPEND=%2, READOUTS=%3, EXPTIME=%4")
                       .arg(m_currentExpInfo.expStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.expEnd.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(m_currentExpInfo.readoutStart.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                       .arg(expTime));
    }

    // 使用Python脚本转换
    QProcess process;
    QString python = "/home/csst/anaconda3/bin/python3";
    QString script = "/home/csst/PS_mosaic/tmp/bin2fits_250522.py";
    QString fitsFile = QString("%1/%2_%3.fits").arg(fitsSavePath).arg(currentBaseFilename).arg(tag);

    process.start(python, {script, binFile, fitsSavePath, currentBaseFilename});

    if (!process.waitForFinished(50000)) {
        LOG_CAMERA("Conversion timed out");
    } else if (process.exitCode() != 0) {
        LOG_CAMERA(process.readAllStandardError());
    } else {
        LOG_CAMERA("Conversion successfully: " + fitsFile);
        QString fitsName = QString("%1_%2.fits").arg(currentBaseFilename).arg(tag);
        recordShootingHistory(fitsName, fitsSavePath); // 记录历史

        // 如果转换成功，更新FITS头信息
        //if (m_currentExpInfo.isValid() && m_currentExpInfo.tag == tag) {
        if (m_currentExpInfo.isValid()) {
            QJsonObject headerObj;
            for (const auto& keyword : keywords) {
                headerObj.insert(QString::fromStdString(keyword.first),
                                 QString::fromStdString(keyword.second));
            }

            // 在后台线程中更新FITS头
            QtConcurrent::run([this, fitsFile, headerObj]() {
                updateFitsHeader(fitsFile, headerObj);
            });
        }

        // 如果是序列拍摄模式，则继续下一个任务
        if (isSequenceRunning && (QString("00") == tag)) {
            QTimer::singleShot(5000, this, &MainWindow::processSequenceNextFrame);
        }
    }
    resetFrameCounter();
}*/

void MainWindow::on_btnmergefits_clicked(){
    QStringList fitsFilePaths;
    QString fitsFile0 = fitsSavePath + "/" + "00" + ".fits";
    QString fitsFile1 = fitsSavePath + "/" + "01" + ".fits";
    QString fitsFile2 = fitsSavePath + "/" + "02" + ".fits";
    QString fitsFile3 = fitsSavePath + "/" + "03" + ".fits";
    fitsFilePaths.append(fitsFile0);
    fitsFilePaths.append(fitsFile1);
    fitsFilePaths.append(fitsFile2);
    fitsFilePaths.append(fitsFile3);

    QString mergedFitsPath = fitsSavePath + "/" + currentBaseFilename + "_merged.fits";
    if (mergeFitsFiles(fitsFilePaths, mergedFitsPath)) {
        LOG_CAMERA(QString("成功合并FITS文件: %1").arg(mergedFitsPath));

        // 可选：删除原始单个FITS文件
        //for (const QString &fitsFile : fitsFilePaths) {
        //QFile::remove(fitsFile);
        //}
    } else {
        LOG_CAMERA("合并FITS文件失败");
    }

}
bool MainWindow::mergeFitsFiles(const QStringList& inputFiles, const QString& outputFile)
{
    fitsfile *outfits = nullptr;
    int status = 0;

    // 删除已存在的输出文件
    remove(outputFile.toLocal8Bit().constData());

    // 创建新的FITS文件
    if (fits_create_file(&outfits, ("!" + outputFile).toLocal8Bit().constData(), &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to create a merged FITS file: %1").arg(errText));
        return false;
    }

    // 逐个添加输入文件作为HDU
    for (const QString &inputFile : inputFiles) {
        fitsfile *infits = nullptr;

        // 打开输入文件
        if (fits_open_file(&infits, inputFile.toLocal8Bit().constData(), READONLY, &status)) {
            char errText[FLEN_STATUS];
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to open the input FITS file: %1 - %2").arg(inputFile).arg(errText));
            fits_close_file(outfits, &status);
            return false;
        }

        // 获取输入HDU信息
        int hdutype;
        if (fits_get_hdu_type(infits, &hdutype, &status)) {
            fits_close_file(infits, &status);
            fits_close_file(outfits, &status);
            return false;
        }

        // 复制HDU到输出文件
        if (fits_copy_hdu(infits, outfits, 0, &status)) {
            char errText[FLEN_STATUS];
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to copy HDU.: %1 - %2").arg(inputFile).arg(errText));
            fits_close_file(infits, &status);
            fits_close_file(outfits, &status);
            return false;
        }

        // 关闭输入文件
        fits_close_file(infits, &status);
    }

    // 添加合并信息到历史记录
    QString history = QString("Merged from %1 files at %2")
                          .arg(inputFiles.size())
                          .arg(QDateTime::currentDateTime().toString(Qt::ISODate));
    fits_write_history(outfits, history.toLocal8Bit().constData(), &status);

    // 关闭输出文件
    if (fits_close_file(outfits, &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to close the merged FITS file.: %1").arg(errText));
        return false;
    }

    return true;
}

// 重置计数器
void MainWindow::resetFrameCounter() {
    frameDoneCount = 0;
    currentTag.clear();
}
/*by sxq 0710
void MainWindow::handleFrameDone(const QString &response)
{
    QStringList parts = response.split(':');
    QString tag;

    foreach (const QString &part, parts) {
        if (part.startsWith("done=")) {
            tag = part.mid(5);
            break;
        }
    }

    if (tag.isEmpty()) {
        LOG_GENERAL("无效的任务标识");
        return;
    }

    QString binFile = QString("%1/%2.bin").arg(binPath).arg(tag);

    // 准备FITS头信息
    std::vector<std::pair<std::string, std::string>> keywords;

    // 1. 添加标准关键字
    keywords.push_back({"INSTRUME", "PS_MOSAIC"});
    keywords.push_back({"OBSERVER", "CSST"});
    keywords.push_back({"TELESCOP", "CSST"});
    keywords.push_back({"EXPTIME", QString::number(currentExposureTime).toStdString()});
    keywords.push_back({"DATE-OBS", QDateTime::currentDateTime().toString(Qt::ISODate).toStdString()});

    // 2. 添加来自IMG_INFO的关键字
    if (!hwStatus.currentImgInfo.isEmpty()) {
        for (auto it = hwStatus.currentImgInfo.begin(); it != hwStatus.currentImgInfo.end(); ++it) {
            QString key = it.key();
            QString value = it.value().isString() ? it.value().toString() :
                                it.value().toVariant().toString();
            keywords.push_back({key.toStdString(), value.toStdString()});
        }
    }
    // 检查并处理文件名
    if (currentBaseFilename.endsWith(".fits", Qt::CaseInsensitive)) {
        currentBaseFilename.chop(5); // 移除最后5个字符(".fits")
        qDebug() << "Trimmed .fits extension:" << currentBaseFilename;
    }
    // 3. 执行转换
    bool success = convertBinToFits(binFile, fitsSavePath, currentBaseFilename + "_" + tag, keywords);

    if (success) {
        LOG_GENERAL("转换成功: " + currentBaseFilename + "_" + tag + ".fits");
        if (isSequenceRunning && (QString("00") == tag)) {
            QTimer::singleShot(5000, this, &MainWindow::processSequenceNextFrame);
        }
    } else {
        LOG_GENERAL("转换失败: " + binFile);
    }

    resetFrameCounter();
}*/

// 开始序列拍摄
void MainWindow::on_btnStartSequence_clicked()
{
    // 检查相机是否就绪
    if (cameraState.status != 0) {
        QMessageBox::warning(this, "警告", "相机当前忙，无法开始序列拍摄");
        return;
    }

    // 检查FITS路径是否设置
    if (fitsSavePath.isEmpty()) {
        QMessageBox::warning(this, "警告", "请先设置FITS文件保存路径");
        return;
    }

    // 选择配置文件
    QString configFile = QFileDialog::getOpenFileName(
        this,
        "选择拍摄序列配置文件",
        QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
        "Text Files (*.txt)"
        );

    if (configFile.isEmpty()) return;

    // 读取配置文件
    QFile file(configFile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "无法打开配置文件: " + file.errorString());
        return;
    }

    sequenceTasks.clear();
    QTextStream in(&file);

    // 读取第一行作为序列名称
    if (!in.atEnd()) {
        currentSequenceName = in.readLine().trimmed();
    }

    // 读取任务
    int lineNum = 1;
    while (!in.atEnd()) {
        LOG_GENERAL(QString("当前读取配置文件: 第%1行").arg(lineNum));
        lineNum++;
        QString line = in.readLine().trimmed();

        LOG_GENERAL("loaded: "+line);

        if (line.isEmpty()) continue;

        // QStringList parts = line.split('\t');
        // QStringList parts = line.split(QRegExp("\s+"),QString::SkipEmptyParts);
        QStringList parts = line.simplified().split(" ");  // handle both space and tab seperators
        if (parts.size() < 5) {
            LOG_GENERAL(QString("警告: 第%1行格式不正确，已跳过").arg(lineNum));
            continue;
        }

        SequenceTask task;
        task.filename = parts[0].trimmed();
        task.expTime = parts[1].toDouble();
        task.dark = parts[2].toInt() != 0;
        task.pinning = parts[3].toInt() != 0;
        task.dump = parts[4].toInt() != 0;

        LOG_GENERAL(QString("==> loaded expTime: %1 s").arg(task.expTime));

        // 验证数据
        if (task.filename.isEmpty() || task.expTime < 0) {
            LOG_GENERAL(QString("警告: 第%1行参数无效，已跳过").arg(lineNum));
            continue;
        }

        sequenceTasks.append(task);
        LOG_GENERAL(QString("当前sequenceTasks size : %1").arg(sequenceTasks.size()));
    }

    file.close();

    if (sequenceTasks.isEmpty()) {
        QMessageBox::warning(this, "警告", "配置文件中没有有效的拍摄任务");
        return;
    }

    // 开始序列
    currentSequenceIndex = -1;
    isSequenceRunning = true;
    ui->btnStartSequence->setEnabled(false);
    ui->btnStopSequence->setEnabled(true);

    ui->btnStartExposure->setEnabled(false);

    LOG_GENERAL(QString("开始连续拍摄序列 [%1]，共%2个任务").arg(currentSequenceName).arg(sequenceTasks.size()));
    processSequenceNextFrame();
}

void MainWindow::on_btnStopSequence_clicked()
{
    if (isSequenceRunning) {
        isSequenceRunning = false;
        m_sequenceAborted = true;
        ui->btnStartSequence->setEnabled(true);
        ui->btnStopSequence->setEnabled(false);

        ui->btnStartExposure->setEnabled(true);
        //ui->labelSequenceProgress->setText("正在停止序列...");
        progressTimer->stop();
        LOG_CAMERA(QString("用户请求停止序列 [%1]，将在当前任务完成后停止").arg(currentSequenceName));
    }
}

void MainWindow::processSequenceNextFrame(){

    LOG_GENERAL(QString("进入processSequenceNextFrame函数，当前连续拍摄序列为 [%1]，共%2个任务").arg(currentSequenceName).arg(sequenceTasks.size()));

    if (!isSequenceRunning || ++currentSequenceIndex >= sequenceTasks.size()) {
        // 序列完成
        LOG_GENERAL("连续拍摄序列已完成");
        sequenceFinished(); // 发射完成信号
        progressTimer->stop();
        ui->labelProgressStatus->setText("序列完成");
        ui->btnStartExposure->setEnabled(true); // added by XYH @ 20250527
        return;
    }

    const SequenceTask &task = sequenceTasks[currentSequenceIndex];

    // 设置基础文件名
    currentBaseFilename = task.filename;
    ui->lineEditBaseFilename->setText(currentBaseFilename);

    // 更新UI显示当前进度
    /*
    ui->labelSequenceProgress->setText(
        QString("正在拍摄: %1/%2 - %3")
            .arg(currentSequenceIndex + 1)
            .arg(sequenceTasks.size())
            .arg(currentBaseFilename)
        );*/

    // 设置曝光参数
    // LOG_GENERAL(QString("@@@@@ expTime: %1 s").arg(task.expTime));

    ui->doubleSpinBoxExposureTime->setValue(task.expTime);
    //ui->checkBoxDarkFrame->setChecked(task.dark);
    ui->checkBoxPinning->setChecked(task.pinning);
    ui->checkBoxDump->setChecked(task.dump);

    // 开始曝光
    on_btnStartExposure_clicked();
    LOG_GENERAL(QString("开始序列任务 %1/%2").arg(currentSequenceIndex+1).arg(sequenceTasks.size()));
}

void MainWindow::sequenceFinished()
{
    // 1. 更新UI状态
    ui->btnStartSequence->setEnabled(true);
    ui->btnStopSequence->setEnabled(false);
    //ui->labelSequenceProgress->setText("序列拍摄已完成");
    ui->btnStartExposure->setEnabled(true); // added by XYH@20250527

    // 2. 重置序列相关变量
    currentSequenceIndex = -1;
    isSequenceRunning = false;
    sequenceTasks.clear();

    // 3. 记录日志
    if(m_sequenceAborted) {
        LOG_GENERAL("用户中断了拍摄序列");
        m_sequenceAborted = false;
    } else {
        LOG_GENERAL("所有拍摄序列任务已完成");
    }

    // 4. 可选: 播放完成音效或显示通知
    QApplication::beep();

    // 5. 恢复相机状态
    if(cameraState.status != 0) {
        sendCameraCommand("abort_290");
    }

    // 6. 更新状态显示
    updateCameraStatus();
}

void MainWindow::checkAndRotateLogFiles()
{
    QMutexLocker locker(&m_logMutex);

    QString currentDate = QDateTime::currentDateTime().toString("yyyyMMdd");

    for (auto category : m_logFiles.keys()) {
        QString filePath = m_logFiles[category]->fileName();
        QString fileDate = filePath.mid(filePath.lastIndexOf('_') - 8, 8);

        if (fileDate != currentDate) {
            // 关闭旧文件
            m_logStreams[category]->flush();
            m_logFiles[category]->close();

            // 创建新文件
            QString newFilePath = m_logDirectory + "/" + currentDate + "_" +
                                  filePath.mid(filePath.lastIndexOf('_') + 1);

            m_logFiles[category]->setFileName(newFilePath);
            if (!m_logFiles[category]->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
                qWarning() << "无法创建新的日志文件:" << newFilePath;
                continue;
            }

            *m_logStreams[category] << QString("[%1] 创建新的日志文件\n")
                                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        }
    }
}
void MainWindow::initializeLogSystem() {
    // 确保日志目录存在
    QDir().mkpath(m_logDirectory);

    // 初始化日志文件
    QStringList logFileNames = {
        "camera_control.log",    // 相机控制日志
        "temp_vacuum.log",       // 温控真空日志
        "alarm.log",             // 报警日志
        "general.log",           // 常规日志
        "debug.log"              // 调试日志
    };

    for (int i = 0; i < logFileNames.size(); ++i) {
        LogCategory category = static_cast<LogCategory>(i);
        QString filePath = m_logDirectory + "/" +
                           QDateTime::currentDateTime().toString("yyyyMMdd_") +
                           logFileNames[i];

        QFile* logFile = new QFile(filePath);
        if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
            qWarning() << "无法打开日志文件:" << filePath;
            continue;
        }

        m_logFiles[category] = logFile;
        m_logStreams[category] = new QTextStream(logFile);
        m_logStreams[category]->setCodec("UTF-8");

        // 写入日志文件头
        *m_logStreams[category] << QString("===== 日志开始 [%1] =====\n")
                                       .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
    }

    // 设置定时刷新缓冲区(每5秒)
    m_logFlushTimer.setInterval(5000);
    connect(&m_logFlushTimer, &QTimer::timeout, this, [this](){
        QMutexLocker locker(&m_logMutex);
        for (auto stream : m_logStreams) {
            stream->flush();
        }
        for (auto file : m_logFiles) {
            file->flush();
        }
    });
    m_logFlushTimer.start();
}
/*
void MainWindow::initializeLogSystem()
{
    QTimer* rotateTimer = new QTimer(this);
    connect(rotateTimer, &QTimer::timeout, this, &MainWindow::checkAndRotateLogFiles);
    rotateTimer->start(3600000); // 每小时检查一次
    // 确保日志目录存在
    QDir().mkpath(m_logDirectory);

    // 初始化四个日志文件
    QStringList logFileNames = {
        "camera_control.log",
        "temp_vacuum.log",
        "alarm.log",
        "general.log"
    };

    for (int i = 0; i < logFileNames.size(); ++i) {
        LogCategory category = static_cast<LogCategory>(i);
        QString filePath = m_logDirectory + "/" + QDateTime::currentDateTime().toString("yyyyMMdd_") + logFileNames[i];

        QFile* logFile = new QFile(filePath);
        if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
            qWarning() << "无法打开日志文件:" << filePath;
            continue;
        }

        m_logFiles[category] = logFile;
        m_logStreams[category] = new QTextStream(logFile);
        m_logStreams[category]->setCodec("UTF-8");
    }

    // 设置定时刷新缓冲区(每5秒)
    m_logFlushTimer.setInterval(5000);
    connect(&m_logFlushTimer, &QTimer::timeout, this, [this](){
        QMutexLocker locker(&m_logMutex);
        for (auto stream : m_logStreams) {
            stream->flush();
        }
        for (auto file : m_logFiles) {
            file->flush();
        }
    });
    m_logFlushTimer.start();
}*/
/*
void MainWindow::initializeLogSystem()
{
    // 设置默认日志目录为程序所在目录下的logs文件夹
    m_logDirectory = QCoreApplication::applicationDirPath() + "/logs";
    QDir().mkpath(m_logDirectory);  // 确保目录存在

    // 创建日志文件名(按日期)
    QString logFileName = QDateTime::currentDateTime().toString("yyyyMMdd") + ".log";
    QString autoupdatelogFileName = QDateTime::currentDateTime().toString("yyyyMMdd") + ".autoupdatelog";
    m_logPath = m_logDirectory + "/" + logFileName;
    m_autoUpdateLogPath = m_logDirectory + "/" + autoupdatelogFileName;

    // 打开日志文件
    m_logFile.setFileName(m_logPath);
    if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        qWarning() << "无法打开日志文件:" << m_logPath;
        return;
    }

    // 设置日志流
    m_logStream.setDevice(&m_logFile);
    m_logStream.setCodec("UTF-8");

    // 设置定时刷新缓冲区(每5秒)
    m_logFlushTimer.setInterval(5000);
    connect(&m_logFlushTimer, &QTimer::timeout, this, [this](){
        m_logStream.flush();
        m_logFile.flush();
    });
    m_logFlushTimer.start();


    m_autoUpdateLogFile.setFileName(m_autoUpdateLogPath);
    if (!m_autoUpdateLogFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        qWarning() << "无法打开自动更新日志文件:" << m_autoUpdateLogPath;
        return;
    }
    LOG_GENERAL("日志系统初始化完成");
}*/

void MainWindow::writeAutoUpdateLog(const QString &message)
{
    if (m_autoUpdateLogFile.isOpen()) {
        QTextStream stream(&m_autoUpdateLogFile);
        stream << QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] ")
               << message << "\n";
    }
}
/*
void MainWindow::writeLog(LogCategory category, const QString& level, const QString& location, const QString& message)
{
    QMutexLocker locker(&m_logMutex);

    if (!m_logFiles.contains(category) || !m_logStreams.contains(category)) {
        qWarning() << "无效的日志类别:" << category;
        return;
    }

    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString fullMessage = QString("[%1][%2][%3] %4\n")
                              .arg(timestamp)
                              .arg(level)
                              .arg(location)
                              .arg(message);

    // 写入对应类别的日志文件
    *m_logStreams[category] << fullMessage;

    // 如果是错误日志，立即刷新
    if (level == "ERROR") {
        m_logStreams[category]->flush();
        m_logFiles[category]->flush();
    }

    // 同时在UI中显示所有日志
    ui->textEditCameraLog->appendPlainText(fullMessage);
}*/
void MainWindow::writeLog(LogCategory category, const QString& level,
                          const QString& location, const QString& message,
                          bool showInUI) {
    QMutexLocker locker(&m_logMutex);

    // 确保日志类别有效
    if (!m_logFiles.contains(category) || !m_logStreams.contains(category)) {
        qWarning() << "无效的日志类别:" << category;
        return;
    }

    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString fullMessage = QString("[%1][%2][%3] %4\n")
                              .arg(timestamp)
                              .arg(level)
                              .arg(location)
                              .arg(message);

    // 写入对应类别的日志文件
    *m_logStreams[category] << fullMessage;

    // 如果是错误日志，立即刷新
    if (level == "ERROR") {
        m_logStreams[category]->flush();
        m_logFiles[category]->flush();
    }

    // 根据标志决定是否在UI中显示
    if (showInUI) {
        // 在主线程更新UI
        QMetaObject::invokeMethod(this, [this, fullMessage]() {
            ui->textEditCameraLog->appendPlainText(fullMessage);
        }, Qt::QueuedConnection);
    }
}
/*
void MainWindow::writeLog(const QString& level, const QString& location, const QString& message)
{
    if (!m_logFile.isOpen()) return;

    QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
    QString fullMessage = QString("[%1][%2][%3] %4\n")
                              .arg(timestamp)
                              .arg(level)
                              .arg(location)
                              .arg(message);

    //m_logStream << fullMessage;
    ui->textEditCameraLog->appendPlainText(fullMessage);
    // 写入文件
    if (m_logFile.isOpen()) {
        m_logStream << fullMessage;
        // 立即刷新(对于关键日志)
        if (level == "ERROR") {
            m_logStream.flush();
            m_logFile.flush();
        }
    }
}*/

void MainWindow::threadSafeLog(const QString& level, const QString& message) {
    QMetaObject::invokeMethod(this, [this, level, message]() {
        QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
        QString logMsg = QString("[%1][%2] %3").arg(timestamp, level, message);

        // UI操作必须在主线程执行
        //ui->textEditCameraLog->appendPlainText(logMsg);

        // 文件写入需要加锁
        QMutexLocker locker(&logMutex);
        if (m_logFile.isOpen()) {
            m_logStream << logMsg << "\n";
        }
    });
}

void MainWindow::onSequenceFinished()
{
    // 序列完成后重置状态，允许开始新序列
    ui->btnStartSequence->setEnabled(true);
    ui->btnStopSequence->setEnabled(false);
    //ui->labelSequenceProgress->setText("序列已完成");

    // 询问是否开始新序列
    QMessageBox::StandardButton reply;
    reply = QMessageBox::question(this, "序列完成", "当前序列已完成，是否开始新序列?",
                                  QMessageBox::Yes|QMessageBox::No);

    if (reply == QMessageBox::Yes) {
        on_btnStartSequence_clicked();
    }
}
void MainWindow::setupPowerControlUI()
{
    // 创建电源控制工具按钮
    powerToolButton = new QToolButton(this);
    powerToolButton->setText(tr("Power Control"));
    powerToolButton->setPopupMode(QToolButton::InstantPopup);
    powerToolButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
    powerToolButton->setStyleSheet("QToolButton { padding: 5px; }");

    // 创建主菜单
    powerMenu = new QMenu(this);
    powerMenu->setStyleSheet(
        "QMenu { background-color: #f0f0f0; border: 1px solid #ccc; }"
        "QMenu::item { padding: 5px 20px 5px 10px; }"
        "QMenu::item:selected { background-color: #d0d0d0; }"
        );

    // 1. 相机电源子菜单
    QMenu* cameraMenu = createPowerSubMenu("Camera Power", ":/icons/camera.png");
    connect(cameraMenu, &QMenu::aboutToShow, [this]() {
        updatePowerButtonStates(btnCameraPowerOn, btnCameraPowerOff, cameraState.status != -1);
    });

    // 2. 加热电源子菜单
    QMenu* heatMenu = createPowerSubMenu("Heat Power", ":/icons/heat.png");
    connect(heatMenu, &QMenu::aboutToShow, [this]() {
        updatePowerButtonStates(btnHeatPowerOn, btnHeatPowerOff, hwStatus.isCooling);
    });

    // 3. 真空电源子菜单
    QMenu* vaccuMenu = createPowerSubMenu("Vaccu Power", ":/icons/vacuum.png");
    //connect(vaccuMenu, &QMenu::aboutToShow, [this]() {
        //updatePowerButtonStates(btnVaccuPowerOn, btnVaccuPowerOff, hwStatus.vacuumPumpRunning);
    //});

    // 添加到主菜单
    powerMenu->addMenu(cameraMenu);
    powerMenu->addMenu(heatMenu);
    powerMenu->addMenu(vaccuMenu);

    // 设置工具按钮的菜单
    powerToolButton->setMenu(powerMenu);

    // 添加到工具栏
    ui->mainToolBar->addWidget(powerToolButton);
}

QMenu* MainWindow::createPowerSubMenu(const QString& title, const QString& iconPath)
{
    QMenu* subMenu = new QMenu(title, this);

    // 设置子菜单样式
    subMenu->setStyleSheet(
        "QMenu { background-color: #f8f8f8; border: 1px solid #ddd; }"
        "QMenu::item { padding: 5px 15px; }"
        "QMenu::item:selected { background-color: #e0e0e0; }"
        );

    // 添加图标
    if (QFile::exists(iconPath)) {
        subMenu->setIcon(QIcon(iconPath));
    }

    // 创建按钮容器
    QWidget* buttonWidget = new QWidget();
    QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget);
    buttonLayout->setContentsMargins(5, 2, 5, 2);
    buttonLayout->setSpacing(5);

    // 创建On/Off按钮
    QPushButton* onButton = new QPushButton("On", buttonWidget);
    QPushButton* offButton = new QPushButton("Off", buttonWidget);

    // 设置按钮样式
    QString buttonStyle =
        "QPushButton {"
        "   padding: 3px 8px;"
        "   border: 1px solid #aaa;"
        "   border-radius: 3px;"
        "}"
        "QPushButton:hover {"
        "   background-color: #e0e0e0;"
        "}"
        "QPushButton:checked {"
        "   background-color: #4CAF50;"
        "   color: white;"
        "}"
        "QPushButton:checked:hover {"
        "   background-color: #45a049;"
        "}";

    onButton->setStyleSheet(buttonStyle);
    offButton->setStyleSheet(buttonStyle);

    // 添加到布局
    buttonLayout->addWidget(onButton);
    buttonLayout->addWidget(offButton);

    // 创建WidgetAction
    QWidgetAction* buttonAction = new QWidgetAction(subMenu);
    buttonAction->setDefaultWidget(buttonWidget);

    // 添加到子菜单
    subMenu->addAction(buttonAction);
    // 添加悬停效果
    subMenu->setAttribute(Qt::WA_TranslucentBackground);
    subMenu->setWindowFlags(subMenu->windowFlags() | Qt::FramelessWindowHint);
    subMenu->setAttribute(Qt::WA_TranslucentBackground);

    // 添加动画效果
    QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(subMenu);
    subMenu->setGraphicsEffect(opacityEffect);
    QPropertyAnimation* animation = new QPropertyAnimation(opacityEffect, "opacity");
    animation->setDuration(150);
    animation->setStartValue(0);
    animation->setEndValue(1);
    animation->start();

    // 返回按钮指针
    if (title.contains("Camera")) {
        btnCameraPowerOn = onButton;
        btnCameraPowerOff = offButton;
    }
    else if (title.contains("Heat")) {
        btnHeatPowerOn = onButton;
        btnHeatPowerOff = offButton;
    }
    else if (title.contains("Vaccu")) {
        btnVaccuPowerOn = onButton;
        btnVaccuPowerOff = offButton;
    }

    return subMenu;
}

void MainWindow::updatePowerButtonStates(QPushButton* onBtn, QPushButton* offBtn, bool isOn)
{
    // 更新按钮选中状态
    onBtn->setChecked(isOn);
    offBtn->setChecked(!isOn);

    // 更新按钮样式
    QString onStyle = onBtn->styleSheet();
    QString offStyle = offBtn->styleSheet();

    if (isOn) {
        onStyle += "background-color: #4CAF50; color: white;";
        offStyle.replace("background-color: #4CAF50;", "");
    } else {
        offStyle += "background-color: #f44336; color: white;";
        onStyle.replace("background-color: #f44336;", "");
    }

    onBtn->setStyleSheet(onStyle);
    offBtn->setStyleSheet(offStyle);
}

void MainWindow::initializeUI()
{
    ui->checkBoxDump->setChecked(true);
    ui->checkBoxPinning->setChecked(true);
    ui->checkboxShowFits->setChecked(true);
    ui->textEditCameraLog->setReadOnly(false);
    ui->textEditCameraLog->clear();
    qApp->setStyleSheet("QCheckBox { font-size: 11pt; }");
    // ui->lblCameraStatus_2->setText("IDLE");
    //  ui->lblCameraStatus_2->setStyleSheet("background-color: green; color: white;");
    ui->lblCurrentTemp_2->setText(QString::number(hwStatus.currentTemp, 'f', 2) + " °C");
    ui->lblCurrentTemp_3->setText(QString::number(hwStatus.currentTemp, 'f', 2) + " °C");
    ui->lblVacuumAlert_2->setText("NORMAL");
    ui->lblVacuumAlert_2->setStyleSheet("color: green; font-weight: bold;");
    // 初始化读出速度选择下拉框
    ui->comboBoxReadoutRate->clear();
    ui->comboBoxReadoutRate->addItem("2M", 0);
    ui->comboBoxReadoutRate->addItem("1M", 1);
    ui->comboBoxReadoutRate->addItem("512K", 2);
    ui->comboBoxReadoutRate->addItem("256K", 3);
    // 添加comBoxframetype初始化
    ui->comBoxframetype->addItem("sci (sc)", "sc");
    ui->comBoxframetype->addItem("bs", "bs");
    ui->comBoxframetype->addItem("dk", "dk");
    ui->comBoxframetype->addItem("ts", "ts");
    ui->comBoxframetype->addItem("sf", "sf");
    ui->comBoxframetype->addItem("df", "df");
    ui->comBoxframetype->addItem("fs", "fs");
    // 添加bin to fits转换方式选择
    //ui->comboConversionMethod->addItem("Python脚本");
    //ui->comboConversionMethod->addItem("C++原生");
    //connect(ui->comboConversionMethod, QOverload<int>::of(&QComboBox::currentIndexChanged),
    // this, &MainWindow::updateConversionMethod);
    // 温度图控制面板
    QWidget *tempControlPanel = new QWidget(this);
    QHBoxLayout *tempControlLayout = new QHBoxLayout(tempControlPanel);

    QLabel *tempTimeLabel = new QLabel("Time Range(Min):", this);
    QSpinBox *tempTimeSpinBox = new QSpinBox(this);
    tempTimeSpinBox->setObjectName("tempTimeRange");
    tempTimeSpinBox->setRange(1, 60);
    tempTimeSpinBox->setValue(60);
    tempTimeSpinBox->setSuffix(" Min");
    tempTimeLabel->setStyleSheet("QLabel {"
                                 "font-size: 12pt;"  // 字体大小
                                 "min-width: 120px;" // 最小宽度
                                 "min-height: 25px;" // 最小高度
                                 "}");

    // 设置SpinBox样式
    tempTimeSpinBox->setStyleSheet("QSpinBox {"
                                   "font-size: 12pt;"
                                   "min-width: 120px;"
                                   "min-height: 25px;"
                                   "}");

    tempControlLayout->addWidget(tempTimeLabel);
    tempControlLayout->addWidget(tempTimeSpinBox);
    tempControlLayout->addStretch(); // 添加伸缩因子使控件左对齐

    // 将温度控制面板添加到温度图上方
    ui->tempChartLayout->insertWidget(0, tempControlPanel);

    // 真空度图控制面板
    QWidget *vacuumControlPanel = new QWidget(this);
    QHBoxLayout *vacuumControlLayout = new QHBoxLayout(vacuumControlPanel);

    QLabel *vacuumTimeLabel = new QLabel("Time Range(Min):", this);
    QSpinBox *vacuumTimeSpinBox = new QSpinBox(this);
    vacuumTimeSpinBox->setObjectName("vacuumTimeRange");
    vacuumTimeSpinBox->setRange(1, 60);
    vacuumTimeSpinBox->setValue(30);
    vacuumTimeSpinBox->setSuffix(" Min");
    vacuumTimeLabel->setStyleSheet("QLabel {"
                                   "font-size: 12pt;"  // 字体大小
                                   "min-width: 120px;" // 最小宽度
                                   "min-height: 25px;" // 最小高度
                                   "}");

    // 设置SpinBox样式
    vacuumTimeSpinBox->setStyleSheet("QSpinBox {"
                                     "font-size: 12pt;"
                                     "min-width: 120px;"
                                     "min-height: 25px;"
                                     "}");

    vacuumControlLayout->addWidget(vacuumTimeLabel);
    vacuumControlLayout->addWidget(vacuumTimeSpinBox);
    vacuumControlLayout->addStretch(); // 添加伸缩因子使控件左对齐

    // 将真空度控制面板添加到真空度图上方
    ui->vacuumChartLayout->insertWidget(0, vacuumControlPanel); // 假设真空度图有自己的布局

    // 连接信号槽
    connect(tempTimeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &MainWindow::updateTempChartTimeRange);
    connect(vacuumTimeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &MainWindow::updateVacuumChartTimeRange);
}
void MainWindow::updateTempChartTimeRange(int minutes)
{
    auto axisX = qobject_cast<QValueAxis*>(hwStatus.multiTempChart->axes(Qt::Horizontal)[0]);
    if (axisX) {
        double maxX = timeCounter / 60.0; // 转换为分钟
        double minX = qMax(0.0, maxX - minutes);
        axisX->setRange(minX, maxX);

        // 可选：自动调整Y轴范围
        updateTemperatureCurves();
    }
}

void MainWindow::updateVacuumChartTimeRange(int minutes)
{
    auto axisX = qobject_cast<QValueAxis*>(vacuumChart->axes(Qt::Horizontal)[0]);
    if (axisX) {
        double maxX = timeCounter / 60.0; // 转换为分钟
        double minX = qMax(0.0, maxX - minutes);
        axisX->setRange(minX, maxX);

        // 可选：自动调整Y轴范围
        auto axisY = qobject_cast<QLogValueAxis*>(vacuumChart->axes(Qt::Vertical)[0]);
        if (axisY) {
            double minY = 8e-4;
            double maxY = qMax(hwStatus.currentVacuum * 1.1, 1e-1);
            axisY->setRange(minY, maxY);
        }
    }
}
void MainWindow::initializeCharts()
{
    // 在创建新系列前清理旧系列
    qDeleteAll(hwStatus.tempSeriesMap);
    hwStatus.tempSeriesMap.clear();

    vacuumChart = new QChart();
    vacuumChart->setTitle("Vacuum Chart");
    vacuumSeries = new QLineSeries();
    vacuumChart->addSeries(vacuumSeries);

    QValueAxis *vacuumAxisX = new QValueAxis();
    vacuumAxisX->setRange(0, 60);
    vacuumAxisX->setTitleText("Time (min)");
    vacuumAxisX->setLabelFormat("%.1f");

    //新的对数坐标
    QLogValueAxis *logAxisY = new QLogValueAxis();
    logAxisY->setRange(1e-5, 1e-2);
    logAxisY->setTitleText("Pressure (Pa)");
    logAxisY->setLabelFormat("%.1e");
    vacuumChart->addAxis(logAxisY, Qt::AlignLeft);
    vacuumSeries->attachAxis(logAxisY);

    vacuumChart->addAxis(vacuumAxisX, Qt::AlignBottom);
    //vacuumChart->addAxis(vacuumAxisY, Qt::AlignLeft);
    vacuumSeries->attachAxis(vacuumAxisX);
    //vacuumSeries->attachAxis(vacuumAxisY);
    /*vacuumSeries->append(0, 2e-5);
    vacuumSeries->append(1, 1e-3);
    vacuumSeries->append(2, 0.8e-2);*/
    ui->vacuumChartView_2->setChart(vacuumChart);
    ui->vacuumChartView_2->setRenderHint(QPainter::Antialiasing);

    // 多温度曲线图表
    hwStatus.multiTempChart = new QChart();
    //hwStatus.multiTempChart->setTitle("Multi Temp Chart");

    // 创建复选框容器
    tempCheckBoxGroup = new QGroupBox("Show", this);
    QVBoxLayout *checkBoxLayout = new QVBoxLayout(tempCheckBoxGroup);

    // 为每个测温点创建曲线系列和复选框
    QStringList tempPoints = {
        "CCD1", "CCD2", "CCD3", "CCD4",
        "Base1", "Base2", "AbsorbCold",
        "Circuit", "Dewar",
        "Backup1", "Backup2","Output","Temp_set"
    };

    QList<QColor> colors = {
        Qt::darkYellow, Qt::green, Qt::blue, Qt::cyan,
        Qt::magenta, Qt::yellow, Qt::darkRed,
        Qt::darkGreen, Qt::darkBlue, Qt::gray,
        Qt::darkCyan, Qt::darkMagenta, Qt::black
    };

    for (int i = 0; i < tempPoints.size(); ++i) {
        // 创建曲线系列
        QtCharts::QLineSeries *series = new QtCharts::QLineSeries();
        series->setName(tempPoints[i]);
        series->setColor(colors[i]);
        hwStatus.tempSeriesMap[tempPoints[i]] = series;
        hwStatus.multiTempChart->addSeries(series);

        // 创建对应的复选框
        QCheckBox *checkBox = new QCheckBox(tempPoints[i], tempCheckBoxGroup);
        checkBox->setChecked(true); // 默认选中
        checkBox->setStyleSheet(QString("color: %1").arg(colors[i].name()));
        tempCheckBoxes[tempPoints[i]] = checkBox;
        checkBoxLayout->addWidget(checkBox);

        // 连接信号槽
        connect(checkBox, &QCheckBox::stateChanged, this, &MainWindow::updateTemperatureCurves);
    }

    // 将复选框容器添加到UI
    ui->verticalLayout->insertWidget(0, tempCheckBoxGroup); // 根据实际布局调整

    // 设置坐标轴
    QValueAxis *axisX = new QValueAxis();
    //axisX->setTitleText("Time (Min)");
    axisX->setRange(0, 60);
    axisX->setLabelFormat("%.1f");
    hwStatus.multiTempChart->addAxis(axisX, Qt::AlignBottom);


    QValueAxis *axisY = new QValueAxis();
    //axisY->setTitleText("Temp (°C)");
    axisY->setRange(-120, 100);
    hwStatus.multiTempChart->addAxis(axisY, Qt::AlignLeft);

    // 将系列附加到坐标轴
    for (auto series : hwStatus.tempSeriesMap) {
        series->attachAxis(axisX);
        series->attachAxis(axisY);
    }

    // 将图表添加到UI
    ui->multiTempChartView->setChart(hwStatus.multiTempChart);
    ui->multiTempChartView->setRenderHint(QPainter::Antialiasing);
}

void MainWindow::handleNewConnection()
{
    while (tcpServer->hasPendingConnections()) {
        QTcpSocket *client = tcpServer->nextPendingConnection();
        QString clientKey = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());

        clientSockets[clientKey] = client;
        LOG_GENERAL("New client connected: " + clientKey);

        connect(client, &QTcpSocket::readyRead, this, &MainWindow::readClientData);
        connect(client, &QTcpSocket::disconnected, this, &MainWindow::clientDisconnected);
    }
}

void MainWindow::onbtnReadTemp() {
    /*if (!hwStatus.isConnected) {
        QMessageBox::warning(this, "警告", "未连接到设备！");
        return;
    }*/

    sendCommandToMCU("temp_read");
}

// 测试真空度查询


void MainWindow::sendCommand(const QString &cmd)
{
    LOG_GENERAL("send command to MCU：  " + cmd);
    if (!mcuSocket || !mcuSocket->isValid()) {
        qCritical() << "UDP Socket Uninitialized";
        LOG_GENERAL("UDP Socket Uninitialized " );
        return;
    }

    // 根据协议文档添加指令头尾
    QString formattedCmd;
    if (cmd == "read_temp") {
        formattedCmd = QString("<ID=301><CamID=1>"); // 状态查询指令
    }
    else if (cmd == "vaccu_read") {
        formattedCmd = QString("<ID=101><Type=VACU>"); // 真空度指令
    }
    else {
        formattedCmd = cmd;
    }
    QByteArray data = formattedCmd.toUtf8();
    qint64 sent = mcuSocket->writeDatagram(data, QHostAddress(mcuIp), mcuPort);

    if (sent == -1) {
        QString error = QString("failed to send [%1]: %2")
                            .arg(QDateTime::currentDateTime().toString("hh:mm:ss.zzz"))
                            .arg(mcuSocket->errorString());
        qDebug() << error;

        // 更新界面错误状态
        // if (ui->lblStatus_2) {
        //  ui->lblStatus_2->setText(error);
        // ui->lblStatus_2->setStyleSheet("color: red;");
        //}
    } else {
        qDebug() << "[" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
        << "] 已发送：" << formattedCmd;
    }
}

void MainWindow::readClientData()
{
    QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());
    if (!client) return;

    while (client->bytesAvailable()) {
        QString command = QString::fromUtf8(client->readAll());
        LOG_GENERAL("Received command: " + command);
        parseProtocol(command, client);
    }
}

void MainWindow::clientDisconnected()
{
    QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());
    if (client) {
        QString clientKey = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
        clientSockets.remove(clientKey);
        LOG_GENERAL("Client disconnected: " + clientKey);
        client->deleteLater();
    }
}

void MainWindow::checkMCUConnectionStatus() {
    QDateTime now = QDateTime::currentDateTime();

    // 温度连接状态
    bool tempConnected = hwStatus.lastTempResponseTime.isValid() &&
                         hwStatus.lastTempResponseTime.msecsTo(now) < 15000;

    // 真空度连接状态
    bool vacuumConnected = hwStatus.lastVacuumResponseTime.isValid() &&
                           hwStatus.lastVacuumResponseTime.msecsTo(now) < 15000;

    bool isValid = hwStatus.lastVacuumResponseTime.isValid();
    double time = hwStatus.lastVacuumResponseTime.msecsTo(now);
    QString clientKey = QString("%1:%2").arg(hwStatus.lastVacuumResponseTime.toString("yyyyMMddhhmmss")).arg(isValid);

    // 更新状态
    hwStatus.tempConnected = tempConnected;
    hwStatus.vacuumConnected = vacuumConnected;
    updateConnectionUI();
}

void MainWindow::updateConnectionUI() {
    // 温度连接状态
    if (hwStatus.tempConnected) {
        //ui->labelTempStatus->setText("已连接");
        ui->lblCurrentTemp_2->setStyleSheet("color: green;");
    } else {
        ui->lblCurrentTemp_2->setText("Not connected");
        ui->lblCurrentTemp_2->setStyleSheet("color: red;");
    }

    // 真空度连接状态
    if (hwStatus.vacuumConnected) {
        //ui->labelVacuumStatus->setText("已连接");
        ui->lblStatus_3->setStyleSheet("color: green;");
    } else {
        ui->lblStatus_3->setText("Not connected");
        ui->lblStatus_3->setStyleSheet("color: red;");
    }
}

void MainWindow::processMCUData() {
    //qint64 elapsedSec = m_startTime.secsTo(QDateTime::currentDateTime());
    while (mcuSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(mcuSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        // 读取UDP数据报
        qint64 bytesRead = mcuSocket->readDatagram(
            datagram.data(),
            datagram.size(),
            &sender,
            &senderPort
            );

        if (bytesRead == -1) {
            writeAutoUpdateLog("读取MCU数据失败: " + mcuSocket->errorString());
            continue;
        }

        QString data = QString(datagram).trimmed();
        LOG_TEMP_VAC("Received MCU response: " + data);

        // 只处理温度查询响应
        if (data.startsWith("CCD_3:")) {
            if (!m_isRegistered) {
                m_isRegistered = true;
                LOG_GENERAL("MCU heartbeat received, registration successful");
            }
            hwStatus.lastTempResponseTime = QDateTime::currentDateTime();
            // 分割数据为键值对（示例数据格式：CCD_1:55.35&&CCD_2:55.48...）
            QStringList tempPairs = data.split("&&");

            double ccdSum = 0.0;
            int ccdCount = 0;

            // 解析每个温度点
            for (const QString &pair : tempPairs) {
                QStringList keyValue = pair.split(':');
                if (keyValue.size() != 2) {
                    writeAutoUpdateLog("无效的温度数据格式: " + pair);
                    continue;
                }

                QString key = keyValue[0].trimmed();
                QString valueStr = keyValue[1].trimmed();
                bool ok;
                double value = valueStr.toDouble(&ok);

                if (!ok) {
                    writeAutoUpdateLog("无效的温度数值: " + valueStr);
                    continue;
                }

                // 更新温度数据到结构体
                if (key == "CCD_1") {
                    hwStatus.tempData.ccd1 = value;
                    ccdSum += value;
                    ccdCount++;
                    //LOG_GENERAL(QString("ccdSum Current: %1 ccdCount: %2").arg(ccdSum).arg(ccdCount));
                } else if (key == "CCD_2") {
                    hwStatus.tempData.ccd2 = value;
                    ccdSum += value;
                    ccdCount++;
                    //LOG_GENERAL(QString("ccdSum Current: %1 ccdCount: %2").arg(ccdSum).arg(ccdCount));
                } else if (key == "CCD_3") {
                    hwStatus.tempData.ccd3 = value;
                    ccdSum += value;
                    ccdCount++;
                    //LOG_GENERAL(QString("ccdSum Current: %1 ccdCount: %2").arg(ccdSum).arg(ccdCount));
                } else if (key == "CCD_4") {
                    hwStatus.tempData.ccd4 = value;
                    ccdSum += value;
                    ccdCount++;
                    //LOG_GENERAL(QString("ccdSum Current: %1 ccdCount: %2").arg(ccdSum).arg(ccdCount));
                } else if (key == "Base_1") {
                    hwStatus.tempData.base1 = value;
                } else if (key == "Base_2") {
                    hwStatus.tempData.base2 = value;
                } else if (key == "Absorbent_cold") {
                    hwStatus.tempData.absorbCold = value;
                } else if (key == "Drive_circuit") {
                    hwStatus.tempData.circuit = value;
                } else if (key == "Dewar") {
                    hwStatus.tempData.dewar = value;
                } else if (key == "Backup1") {
                    hwStatus.tempData.backup1 = value;
                } else if (key == "Backup2") {
                    hwStatus.tempData.backup2 = value;
                } else if (key == "output") {
                    hwStatus.tempData.outputPower = value;
                } else if (key == "temp_set") {
                    hwStatus.tempData.targetTemp = value;
                } else {
                    writeAutoUpdateLog("未知的温度点: " + key);
                }
            }

            // 计算CCD1-CCD4的平均温度
            if (ccdCount > 0) {
                hwStatus.currentTemp = ccdSum / ccdCount;
                //LOG_GENERAL(QString("ccdSum Current: %1 ccdCount: %2").arg(ccdSum).arg(ccdCount));

                double tempDiff = qAbs(hwStatus.currentTemp - hwStatus.tempData.targetTemp);
                hwStatus.tempStabilized = (tempDiff <= 1.0); // 温差不超过1℃视为稳定
                QString style;
                if (hwStatus.tempStabilized) {
                    style = "color: blue; font-weight: bold;"; // 稳定时蓝色
                } else {
                    style = "color: red; font-weight: bold;"; // 不稳定时黄色
                }
                LOG_TEMP_VAC(QString("hwStatus.currentTemp: %1 ").arg(hwStatus.currentTemp));
                ui->lblCurrentTemp_2->setStyleSheet(style);
                ui->lblCurrentTemp_3->setStyleSheet(style);
                ui->lblCurrentTemp_2->setText(QString::number(hwStatus.currentTemp, 'f', 2) + " °C");
                ui->lblCurrentTemp_3->setText(QString::number(hwStatus.currentTemp, 'f', 2) + " °C");
            }

            // 触发UI更新
            QMetaObject::invokeMethod(this, &MainWindow::updateTemperatureCurves);
            QMetaObject::invokeMethod(this, &MainWindow::updateTemperatureDisplay);
            QMetaObject::invokeMethod(this, &MainWindow::updateHeartbeatStatus);
        }
        else if (data.startsWith("vaccu_read:")) {  // 协议前缀是"vaccu_read:"

            QString payload = data.mid(11).trimmed();  // 去掉前缀
            if (payload.length() >= 15) {  // 确保数据长度足够
                hwStatus.lastVacuumResponseTime = QDateTime::currentDateTime();
                bool ok = true;
                // 提取有效部分（11-16位）
                QString valuePart = payload.mid(10, 6);  // 从0开始计数，11-16位是索引10-15
                if (valuePart.length() == 6) {
                    // 解析有效数字（前4位）
                    double mantissa = valuePart.left(4).toDouble(&ok);
                    ok = true;
                    if (ok) {
                        // 解析指数（后2位减去21）
                        int exponent = valuePart.mid(4, 2).toInt(&ok) - 21;
                        //QString debuginfo= QString("有效部分%1 有效数字%2 指数%3").arg(valuePart).arg(mantissa).arg(exponent);
                        //writeAutoUpdateLog("真空度debuginfo" + debuginfo);
                        if (ok) {
                            // 计算最终真空度值
                            double vacuum = mantissa * qPow(10, exponent);
                            hwStatus.currentVacuum = vacuum;
                            hwStatus.vacuumHistory.append(vacuum);
                            LOG_TEMP_VAC(QString("hwStatus.currentVacuum: %1 ").arg(hwStatus.currentVacuum));

                            // 限制历史数据长度（如保留最近1小时数据，每秒1个点）
                            if (hwStatus.vacuumHistory.size() > 3600) {
                                hwStatus.vacuumHistory.removeFirst();
                            }
                            //updateVacuumData();
                        }
                    }

                }
            }
            // 触发UI更新
            QMetaObject::invokeMethod(this, &MainWindow::updateVacuumData);
        }
        else if (data.startsWith("temp_set=:")){
            writeAutoUpdateLog("温度set to:" + data);
        }
    }
}

void MainWindow::updateVacuumData()
{
    //static int timeCounter = 0;
    double xValue = timeCounter / 60.0; // 转换为分钟

    // 添加新数据点
    vacuumSeries->append(xValue, hwStatus.currentVacuum);

    if (hwStatus.currentVacuum != 1000) {
        ui->lblStatus_3->setText(QString::number(hwStatus.currentVacuum, 'e', 3));
    }

    //ui->lblStatus_4->setText(QString::number(hwStatus.currentVacuum, 'e', 3));
    //ui->lblStatus_5->setText(QString::number(hwStatus.currentVacuum, 'e', 3));
    // 动态调整X轴范围，显示最近60分钟的数据
    auto axisX = qobject_cast<QValueAxis*>(vacuumChart->axes(Qt::Horizontal)[0]);
    if (axisX) {
        double minX = qMax(0.0, xValue - 60.0); // 显示最近60分钟
        double maxX = xValue;
        axisX->setRange(minX, maxX);
    }

    // 动态调整Y轴范围（对数坐标）
    auto axisY = qobject_cast<QLogValueAxis*>(vacuumChart->axes(Qt::Vertical)[0]);
    if (axisY) {
        // 自动计算Y轴范围（保留10%边距）
        double minY = 8e-4; // 最小固定为8e-4 Pa
        double maxY = qMax(hwStatus.currentVacuum * 1.1, 1e-2); // 当前值的110%或1e-2 Pa
        axisY->setRange(minY, maxY);
    }

    // 可选：限制数据点数量（如果性能有问题）
    /*
    if (timeCounter > 3600 * 2) { // 保留2小时数据
        vacuumSeries->removePoints(0, vacuumSeries->count() - 3600 * 2);
    }
    */
    // 检查报警
    //checkAllAlarms();
}
void MainWindow::updateTemperatureData()
{
    //static int timeCounter = 0;
    double xValue = timeCounter / 60.0; // 转换为分钟

    // 更新所有曲线的数据
    hwStatus.tempSeriesMap["CCD1"]->append(xValue, hwStatus.tempData.ccd1);
    hwStatus.tempSeriesMap["CCD2"]->append(xValue, hwStatus.tempData.ccd2);
    hwStatus.tempSeriesMap["CCD3"]->append(xValue, hwStatus.tempData.ccd3);
    hwStatus.tempSeriesMap["CCD4"]->append(xValue, hwStatus.tempData.ccd4);
    hwStatus.tempSeriesMap["Base1"]->append(xValue, hwStatus.tempData.base1);
    hwStatus.tempSeriesMap["Base2"]->append(xValue, hwStatus.tempData.base2);
    hwStatus.tempSeriesMap["AbsorbCold"]->append(xValue, hwStatus.tempData.absorbCold);
    hwStatus.tempSeriesMap["Circuit"]->append(xValue, hwStatus.tempData.circuit);
    hwStatus.tempSeriesMap["Dewar"]->append(xValue, hwStatus.tempData.dewar);
    hwStatus.tempSeriesMap["Backup1"]->append(xValue, hwStatus.tempData.backup1);
    hwStatus.tempSeriesMap["Backup2"]->append(xValue, hwStatus.tempData.backup2);
    hwStatus.tempSeriesMap["Output"]->append(xValue, hwStatus.tempData.outputPower);
    hwStatus.tempSeriesMap["Temp_set"]->append(xValue, hwStatus.tempData.targetTemp);


    timeCounter++;

    // 动态调整X轴范围，显示最近60分钟的数据
    auto axisX = qobject_cast<QValueAxis*>(hwStatus.multiTempChart->axes(Qt::Horizontal)[0]);
    if (axisX) {
        double minX = qMax(0.0, xValue - 60.0); // 显示最近60分钟
        double maxX = xValue;
        axisX->setRange(minX, maxX);
    }

    // 限制数据点数量（可选，如果性能有问题时启用）
    /*
    if (timeCounter > 3600 * 2) { // 保留2小时数据
        for (auto &series : hwStatus.tempSeriesMap) {
            if (series->count() > 3600 * 2) {
                series->removePoints(0, series->count() - 3600 * 2);
            }
        }
    }
    */
    // 检查报警
    //checkAllAlarms();
}
void MainWindow::updateTemperatureCurves()
{
    // 只控制曲线的可见性，不影响数据更新
    for (auto &series : hwStatus.tempSeriesMap) {
        bool visible = tempCheckBoxes[series->name()]->isChecked();
        series->setVisible(visible);
    }

    // 自动调整Y轴范围
    auto yAxis = qobject_cast<QValueAxis*>(hwStatus.multiTempChart->axes(Qt::Vertical)[0]);
    if (yAxis) {
        double min = std::numeric_limits<double>::max();
        double max = std::numeric_limits<double>::lowest();

        // 只考虑当前可见的曲线
        for (const auto &series : hwStatus.tempSeriesMap) {
            if (series->isVisible()) {
                auto points = series->pointsVector();
                for (const QPointF &point : points) {
                    if (!qIsNaN(point.y())) {
                        min = qMin(min, point.y());
                        max = qMax(max, point.y());
                    }
                }
            }
        }

        // 设置Y轴范围（保留10%边距）
        if (min < max) {
            double margin = (max - min) * 0.1;
            yAxis->setRange(min - margin, max + margin);
        }
    }
}

void MainWindow::updateTemperatureDisplay() {
    // CCD温度
    ui->labelCCD1->setText(formatTemperature(hwStatus.tempData.ccd1));
    ui->labelCCD2->setText(formatTemperature(hwStatus.tempData.ccd2));
    ui->labelCCD3->setText(formatTemperature(hwStatus.tempData.ccd3));
    ui->labelCCD4->setText(formatTemperature(hwStatus.tempData.ccd4));

    // 基板温度
    ui->labelBase1->setText(formatTemperature(hwStatus.tempData.base1));
    ui->labelBase2->setText(formatTemperature(hwStatus.tempData.base2));

    // 吸收体与电路
    ui->labelAbsorbCold->setText(formatTemperature(hwStatus.tempData.absorbCold));
    ui->labelCircuit->setText(formatTemperature(hwStatus.tempData.circuit));
    //ui->labelCircuit2->setText(formatTemperature(hwStatus.tempData.circuit2));

    // 其他设备
    ui->labelDewar->setText(formatTemperature(hwStatus.tempData.dewar));
    ui->labelBackup1->setText(formatTemperature(hwStatus.tempData.backup1));
    ui->labelBackup2->setText(formatTemperature(hwStatus.tempData.backup2));

    // 功率与目标温度
    ui->labelOutput->setText(QString::number(hwStatus.tempData.outputPower, 'f', 2) + " %");
    ui->labelTarget->setText(QString::number(hwStatus.tempData.targetTemp, 'f', 2) + " °C");
    ui->labelTarget_2->setText(QString::number(hwStatus.tempData.targetTemp, 'f', 2) + " °C");
    ui->spinTemperature_2->setValue(hwStatus.tempData.targetTemp);
}

// 辅助函数：格式化温度显示（处理NaN）
QString MainWindow::formatTemperature(double temp) {
    if (qIsNaN(temp)) {
        return "N/A";
    } else {
        return QString::number(temp, 'f', 2) + " °C";
    }
}
// 收集手动拍摄的头信息
std::vector<std::pair<std::string, std::string>> MainWindow::collectManualHeaderInfo() {
    std::vector<std::pair<std::string, std::string>> keywords;

    // 标准关键字
    keywords.push_back({"INSTRUME", "BLUE"});
    keywords.push_back({"OBSERVER", "XYH"});
    keywords.push_back({"TELESCOP", "MEPHISTO"});
    keywords.push_back({"EXPTIME", QString::number(ui->doubleSpinBoxExposureTime->value()).toStdString()});
    keywords.push_back({"DATE-OBS", QDateTime::currentDateTime().toString(Qt::ISODate).toStdString()});

    // 从界面获取手动输入的信息
    if (!ui->lineEditObjName->text().isEmpty()) {
        keywords.push_back({"OBJ_NAME", ui->lineEditObjName->text().toStdString()});
    }
    if (!ui->lineEditFwName->text().isEmpty()) {
        keywords.push_back({"FW_NAME", ui->lineEditFwName->text().toStdString()});
    }
    if (!ui->lineEditImgNum->text().isEmpty()) {
        keywords.push_back({"IMG_NUM", ui->lineEditImgNum->text().toStdString()});
    }
    QString frameType = ui->comBoxframetype->currentData().toString();
    std::string darkMode = (frameType == "bs" || frameType == "dk") ? "1" : "0";
    // 其他复选框状态

    if (m_currentExpInfo.isValid()) {
        keywords.push_back({"RATE", m_currentExpInfo.readoutRate.toStdString()});
    }
    keywords.push_back({"FRAMETYPE", frameType.toStdString()});
    keywords.push_back({"DARK", darkMode});
    keywords.push_back({"PINNING", ui->checkBoxPinning->isChecked() ? "1" : "0"});
    keywords.push_back({"DUMP", ui->checkBoxDump->isChecked() ? "1" : "0"});

    return keywords;
}
void MainWindow::parseProtocol(const QString &command, QTcpSocket *client)
{
    // 解析指令格式: <ID=xxx><PARAM1=value1><PARAM2=value2>;
    QRegularExpression re("<([^>]+)>");
    QRegularExpressionMatchIterator i = re.globalMatch(command);

    QMap<QString, QString> params;
    while (i.hasNext()) {
        QRegularExpressionMatch match = i.next();
        QString segment = match.captured(1);
        int eqPos = segment.indexOf('=');
        if (eqPos != -1) {
            QString key = segment.left(eqPos).trimmed();
            QString value = segment.mid(eqPos+1).trimmed();
            params[key] = value;
        }
    }

    // 根据ID分发处理
    QString cmdId = params.value("ID");
    if (cmdId == "300") {
        handleCommand300(params, client);
    } else if (cmdId == "301") {
        handleCommand301(params, client);
    } else if (cmdId == "302") {
        handleCommand302(params, client);
    } else if (cmdId == "303") {
        handleCommand303(params, client);
    } else if (cmdId == "304") {
        handleCommand304(params, client);
    } else {
        sendResponse(QString("<ID=%1><INFO=Error><MSG=Unsupported_command>").arg(cmdId), client);
    }
}

void MainWindow::handleCommand300(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 格式: <ID=300><CamID=25078><INI=1><COOLING=1><COOL_TEM=-65>
    if (!params.contains("CamID") || !params.contains("COOLING") || !params.contains("COOL_TEM")) {
        sendResponse("<ID=300><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    QString camId = params["CamID"];
    int cooling = params["COOLING"].toInt();
    double targetTemp = params["COOL_TEM"].toDouble();

    // 1. 先返回确认响应
    sendResponse("<ID=300><INFO=OK>", client);

    // 2. 发送温控指令
    QString tempCmd = QString("temp_set:%1").arg(targetTemp);
    sendCommandToMCU(tempCmd);

    // 3. 异步等待温度稳定后返回最终响应
    QTimer::singleShot(1000, [this, client, camId]() {
        QString response = QString("<ID=300><CamID=%1><INFO=OK><ACTION=DONE><INI=1><CUR_TEM=%2><SENDTIME=%3>")
        .arg(camId)
            .arg(hwStatus.currentTemp, 0, 'f', 2)  // 使用CCD1-CCD4的平均温度
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
        sendResponse(response, client);
    });
}
void MainWindow::handleCommand301(const QMap<QString, QString> &params, QTcpSocket *client)
{
    /*
    协议规范：
    客户端指令格式: <ID=301><CamID=%s>;
    服务端响应格式:
      1. 先返回确认: <ID=301><INFO=OK>
      2. 实际状态返回:
         <ID=301><INFO=%s><ACTION=DONE><STATUS=%d><CUR_TEM=%f><TEM_STATUS=%d><SENDTIME=...>;
      特殊字段:
        STATUS: 相机状态(0-5)
        TEM_STATUS: 制冷稳定状态(0=不稳定,1=稳定)
    */

    //  参数校验
    if (!params.contains("CamID")) {
        sendResponse("<ID=301><INFO=Error><MSG=Missing_CamID>", client);
        return;
    }

    QString camId = params["CamID"];
    // 1. 发送温度查询指令
    sendCommandToMCU("temp_read");

    // 2. 立即返回确认响应
    sendResponse("<ID=301><INFO=OK>", client);
    // 3. 异步等待温度数据更新后返回最终响应
    QTimer::singleShot(500, [this, client, camId]() {
        //QString response = QString("<ID=301><CamID=%1><INFO=OK><ACTION=DONE><STATUS=%2><CUR_TEM=%3><CUR_VACCUM=%4><TEM_STATUS=%5><SENDTIME=%6>")
        QString response = QString("<ID=301><CamID=%1><INFO=OK><ACTION=DONE><STATUS=%2><CUR_TEM=%3><TEM_STATUS=%4><SENDTIME=%5>")
        .arg(camId)
            .arg(static_cast<int>(hwStatus.cameraState))
            .arg(hwStatus.currentTemp, 0, 'f', 2)  // 使用CCD1-CCD4的平均温度
            //.arg(QString::number(hwStatus.currentVacuum, 'e', 3))
            .arg(hwStatus.tempStabilized ? 1 : 0)
            .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        sendResponse(response, client);
    });

    // 4. 状态记录（调试用）
    LOG_CAMERA(QString("Camera status query response [Cam:%1][Status:%2][Temp:%3][Stable:%4]")
                   .arg(camId)
                   .arg(hwStatus.cameraState)
                   .arg(hwStatus.currentTemp)
                   .arg(hwStatus.tempStabilized));
}

void MainWindow::handleCommand302(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 1. 参数校验
    if (!params.contains("CamID") || !params.contains("IMAGENAME") ||
        !params.contains("TIME") || !params.contains("EXP_TYPE") ||
        !params.contains("IMG_INFO")) {
        sendResponse("<ID=302><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    // 2. 检查相机状态
    if (cameraState.status != Idle) {
        //sendResponse("<ID=302><INFO=Error><MSG=Camera is busy>", client);
        //return;
    }

    // 3. 解析参数
    QString camId = params["CamID"];
    QString imageName = params["IMAGENAME"];
    double expTime = params["TIME"].toDouble();
    QString expType = params["EXP_TYPE"];
    QString imgInfo = params["IMG_INFO"];
    bool dump = params.value("DUMP", "0") == "1";
    bool pinning = params.value("PINNING", "0") == "1";


    int index = -1;
    if (expType == "sc") {
        index = ui->comBoxframetype->findData("sc");
    } else if (expType == "bs") {
        index = ui->comBoxframetype->findData("bs");
    } else if (expType == "dk") {
        index = ui->comBoxframetype->findData("dk");
    } else if (expType == "ts") {
        index = ui->comBoxframetype->findData("ts");
    } else if (expType == "sf") {
        index = ui->comBoxframetype->findData("sf");
    } else if (expType == "df") {
        index = ui->comBoxframetype->findData("df");
    } else if (expType == "fs") {
        index = ui->comBoxframetype->findData("fs");
    }
    if (index != -1) {
        QMetaObject::invokeMethod(this, [this, index]() {
            ui->comBoxframetype->setCurrentIndex(index);
        }, Qt::QueuedConnection);
    }

    // 4. 立即返回确认响应
    sendResponse("<ID=302><INFO=OK>", client);

    // 5. 记录曝光开始时间（ISO 8601格式）
    QDateTime expStartTime = QDateTime::currentDateTime();
    QString expStartTimeStr = expStartTime.toString("yyyy-MM-dd hh:mm:ss.zzz");

    // 6. 设置曝光参数
    currentBaseFilename = imageName;
    ui->lineEditBaseFilename->setText(currentBaseFilename);
    ui->doubleSpinBoxExposureTime->setValue(expTime);
    currentExposureTime = expTime;
    currentProgressTotalTime = expTime + 10; // 曝光+读出时间估算
    progressStartTime = QDateTime::currentMSecsSinceEpoch();

    // 7. 解析并保存IMG_INFO
    QJsonParseError jsonError;
    QJsonDocument doc = QJsonDocument::fromJson(imgInfo.toUtf8(), &jsonError);
    if (!doc.isNull() && doc.isObject()) {
        QJsonObject imgInfoObj = doc.object();
        if (imgInfoObj.contains("IMG_NUM")) {
            // 更新UI中的IMG_NUM显示
            QString imgNum = imgInfoObj["IMG_NUM"].toString();
            ui->lineEditImgNum->setText(imgNum);
            QString objectname = imgInfoObj["OBJ_NAME"].toString();
            ui->lineEditObjName->setText(objectname);
            QString fwname = imgInfoObj["FW_NAME"].toString();
            ui->lineEditFwName->setText(fwname);
            // 同时更新本地计数器，保持同步
            m_localImageCounter = imgNum.toInt();
        }
        hwStatus.currentImgInfo = imgInfoObj;
    } else {
        LOG_CAMERA(QString("IMG_INFO Parsing failed: %1").arg(jsonError.errorString()));
    }

    int darkMode = (expType == "bs" || expType == "dk") ? 1 : 0;

    // 8. 发送相机曝光指令（通过UDP）
    QString tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    QString expCmd = QString("get_frame_290:$tag=%1$dark=%2$pinning=%3$dump=%4$exp=%5")
                         .arg(tag)
                         .arg(darkMode)  // 暗场标记
                         .arg(pinning ? 1 : 0)
                         .arg(dump ? 1 : 0)
                         .arg(expTime, 0, 'f', 2);
    sendCameraCommand(expCmd);

    // 存储响应信息
    QString tag1 = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    m_pendingResponse = {
        client,
        params["CamID"],
        params["TIME"].toDouble(),
        QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"),
        params["IMAGENAME"]
    };

    // 9. 更新状态
    //hwStatus.cameraState = Exposing;
    //progressTimer->start();
    // 初始化进度控制
    currentPhase = PHASE_EXPOSING;
    progressStartTime = QDateTime::currentMSecsSinceEpoch();
    currentExposureTime = expTime;

    // 设置进度条范围
    ui->ExposureprogressBar->setRange(0, 100);
    ui->ExposureprogressBar->setValue(0);

    // 启动定时器
    progressTimer->start(200); // 200ms更新一次

    // 10. 记录日志
    LOG_CAMERA(QString("Start exposure [Cam:%1][Exp:%2s][Type:%3][File:%4]")
                    .arg(camId).arg(expTime).arg(expType).arg(imageName));

}

void MainWindow::handleCommand303(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 格式: <ID=303><CamID=25078>
    if (!params.contains("CamID")) {
        sendResponse("<ID=303><INFO=Error><MSG=Missing_CamID>", client);
        return;
    }

    // 1. 返回确认响应
    sendResponse("<ID=303><INFO=OK>", client);

    // 2. 发送中止曝光指令
    sendCameraCommand("abort_290");

    // 3. 更新状态
    //hwStatus.cameraState = MainWindow::Idle;
    progressTimer->stop();
    ui->ExposureprogressBar->setValue(0);

    // 4. 返回完成响应
    QString response = QString("<ID=303><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
                           .arg(params["CamID"])
                           .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
    sendResponse(response, client);
}
/*
void MainWindow::handleCommand304(const QMap<QString, QString> &params, QTcpSocket *client)
{

        // 1. 参数校验
        if (!params.contains("CamID") || !params.contains("FITS_Header") || !params.contains("FITS_Name")) {
            throw std::runtime_error("缺少必要参数");
        }

        QString camId = params["CamID"];
        QString fitsHeader = params["FITS_Header"];
        QString fitsName = params["FITS_Name"];

        // 2. 验证JSON格式
        QJsonParseError jsonError;
        QJsonDocument doc = QJsonDocument::fromJson(fitsHeader.toUtf8(), &jsonError);
        if (doc.isNull()) {
            throw std::runtime_error(QString("无效的JSON格式: %1 (位置:%2)")
                                         .arg(jsonError.errorString())
                                         .arg(jsonError.offset).toStdString());
        }

        // 3. 构建文件路径
        QString fitsFilePath = QDir(fitsSavePath).absoluteFilePath(fitsName);
        if (!fitsFilePath.endsWith(".fits")) {
            fitsFilePath += ".fits";
        }

        // 4. 验证文件存在性
        if (!QFileInfo(fitsFilePath).exists()) {
            throw std::runtime_error(QString("文件不存在: %1").arg(fitsFilePath).toStdString());
        }
        // 5. 解析FITS Header
        QJsonObject headerObj = QJsonDocument::fromJson(fitsHeader.toUtf8()).object();
        if (headerObj.isEmpty()) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=Invalid_FITS_Header><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponse(errorResponse, client);
            return;
        }
        // 6. 同步更新FITS头信息（在主线程执行）
        bool success = false;
        QString errorMsg;
        success = updateFitsHeader(fitsFilePath, headerObj);
        // 7. 构造响应
        QString response;
        if (success) {
            response = QString("<ID=304><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        } else {
            if (errorMsg.isEmpty()) errorMsg = "头信息更新失败";
            response = QString("<ID=304><CamID=%1><INFO=Error><MSG=%2><SENDTIME=%3>;")
                           .arg(camId)
                           .arg(errorMsg)
                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        }

        // 8. 发送响应
        sendResponse(response, client);

        LOG_GENERAL(QString("FITS头信息更新 [Cam:%1][File:%2][Success:%3]")
                     .arg(camId).arg(QFileInfo(fitsFilePath).fileName()).arg(success));

        // 6. 在后台线程中更新FITS头信息

        //QtConcurrent::run([this, fitsFilePath, headerObj, camId, client]() {
            //bool success = updateFitsHeader(fitsFilePath, headerObj);

            // 7. 构造最终响应
            //QString response;
            //if (success) {
                //response = QString("<ID=304><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
                //.arg(camId)
                //    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            //} else {
              //  response = QString("<ID=304><CamID=%1><INFO=Error><MSG=Header_update_failed><SENDTIME=%2>;")
               // .arg(camId)
                //    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
           // }

            // 注意：需要在主线程发送响应
            //QMetaObject::invokeMethod(this, [this, client, response]() {
               // sendResponse(response, client);
            //});

           // LOG_GENERAL(QString("FITS头信息更新 [Cam:%1][File:%2][Success:%3]")
            //             .arg(camId).arg(QFileInfo(fitsFilePath).fileName()).arg(success));
       // });
}*/
void MainWindow::handleCommand304(const QMap<QString, QString>& params, QTcpSocket* client)
{
    // 1. 参数校验
    if (!params.contains("CamID") || !params.contains("FITS_Header") || !params.contains("FITS_Name")) {
        sendResponse("<ID=304><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    QString camId = params["CamID"];
    QString fitsHeader = params["FITS_Header"];
    QString fitsName = params["FITS_Name"];

    // 2. 立即返回确认响应
    sendResponse("<ID=304><INFO=OK>", client);

    // 3. 在后台线程中处理FITS文件和计算中值
    QtConcurrent::run([this, camId, fitsHeader, fitsName, client]() {
        // 解析JSON头信息
        QJsonParseError jsonError;
        QJsonDocument doc = QJsonDocument::fromJson(fitsHeader.toUtf8(), &jsonError);
        if (doc.isNull()) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=Invalid_JSON_format><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponse(errorResponse, client);
            return;
        }

        QJsonObject headerObj = doc.object();

        // 构建完整文件路径
        QString fitsFilePath = QDir(fitsSavePath).absoluteFilePath(fitsName);
        if (!fitsFilePath.endsWith(".fits")) {
            fitsFilePath += ".fits";
        }

        // 检查文件是否存在
        if (!QFile::exists(fitsFilePath)) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=File_not_found><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponse(errorResponse, client);
            return;
        }

        // 计算图像中值（仅对平场类型）
        double fitsMedian = -999.0; // 默认非平场值
        if (headerObj.contains("IMAGETYP")) {
            QString imageType = headerObj["IMAGETYP"].toString().toLower();
            if (imageType == "flat" || imageType == "df" || imageType == "ff") { // 平场类型
                try {
                    fitsMedian = calculateFitsMedian(fitsFilePath);
                    /*LOG_GENERAL(QString("计算平场中值完成 [Cam:%1][File:%2][Median:%3]")
                                 .arg(camId)
                                 .arg(QFileInfo(fitsFilePath).fileName())
                                 .arg(fitsMedian));*/
                    threadSafeLog("INFO", QString("计算平场中值完成 [Cam:%1][Median:%2]")
                                              .arg(camId).arg(fitsMedian));
                } catch (const std::exception& e) {
                    LOG_GENERAL(QString("calculateFitsMedian failed: %1").arg(e.what()));
                }
            }
        }

        // 更新FITS头信息
        bool headerUpdated = updateFitsHeader(fitsFilePath, headerObj);

        // 构造最终响应
        QString response = QString("<ID=304><CamID=%1><INFO=%2><ACTION=DONE><Fits_Median=%3><SENDTIME=%4>;")
                               .arg(camId)
                               .arg(headerUpdated ? "OK" : "Error")
                               .arg(fitsMedian, 0, 'f', 2)
                               .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));

        // 在主线程发送响应
        QMetaObject::invokeMethod(this, [this, client, response]() {
            sendResponse(response, client);
        });
    });
}

double MainWindow::calculateFitsMedian(const QString& fitsPath)
{
    fitsfile* fitsFile = nullptr;
    int status = 0;
    long fpixel[2] = {1, 1};
    long naxes[2] = {0, 0};
    double median = -999.0;

    // 1. 打开FITS文件
    QByteArray filePath = QDir::toNativeSeparators(fitsPath).toLocal8Bit();
    if (fits_open_file(&fitsFile, filePath.constData(), READONLY, &status)) {
        char errText[FLEN_STATUS];
        fits_get_errstatus(status, errText);
        throw std::runtime_error(QString("打开FITS文件失败: %1").arg(errText).toStdString());
    }

    // 2. 获取图像尺寸
    if (fits_get_img_size(fitsFile, 2, naxes, &status)) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("获取图像尺寸失败");
    }

    long npixels = naxes[0] * naxes[1];
    if (npixels <= 0) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("无效的图像尺寸");
    }

    // 3. 读取图像数据
    std::vector<float> buffer(npixels);
    if (fits_read_pix(fitsFile, TFLOAT, fpixel, npixels, nullptr, buffer.data(), nullptr, &status)) {
        fits_close_file(fitsFile, &status);
        throw std::runtime_error("读取图像数据失败");
    }

    // 4. 计算中值
    auto mid = buffer.begin() + npixels / 2;
    std::nth_element(buffer.begin(), mid, buffer.end());
    median = *mid;

    // 如果是偶数个像素，取中间两个的平均值
    if (npixels % 2 == 0) {
        auto mid2 = std::max_element(buffer.begin(), mid);
        median = (*mid + *mid2) / 2.0;
    }

    // 5. 关闭文件
    fits_close_file(fitsFile, &status);

    return median;
}

bool MainWindow::updateFitsHeader(const QString& fitsPath, const QJsonObject& headerData)
{
    fitsfile* fitsFile = nullptr;
    int status = 0;
    char errText[FLEN_STATUS] = {0};

    // 1. 打开文件
    QByteArray filePath = QDir::toNativeSeparators(fitsPath).toLocal8Bit();
    if (fits_open_file(&fitsFile, filePath.constData(), READWRITE, &status)) {
        fits_get_errstatus(status, errText);
        LOG_CAMERA(QString("Failed to open the FITS file.: %1").arg(errText));
        return false;
    }

    // 2. 更新关键字
    for (auto it = headerData.begin(); it != headerData.end(); ++it) {
        QString key = it.key();
        QString value = it.value().isString() ? it.value().toString()
                                              : it.value().toVariant().toString();

        QByteArray keyData = key.toLocal8Bit();
        QByteArray valueData = value.toLocal8Bit();

        // 先删除已存在的关键字（忽略错误）
        fits_delete_key(fitsFile, keyData.constData(), &status);
        status = 0;

        // 写入新关键字
        if (fits_update_key(fitsFile, TSTRING,
                            keyData.constData(),
                            valueData.data(),  // 注意这里用 data() 而不是 constData()
                            nullptr, &status)) {
            fits_get_errstatus(status, errText);
            LOG_CAMERA(QString("Failed to update the keyword: %1").arg(errText));
            status = 0;
            continue;
        }
    }

    // 3. 关闭文件
    if (fits_close_file(fitsFile, &status)) {
        fits_get_errstatus(status, errText);
        LOG_GENERAL(QString("Failed to close the FITS file: %1").arg(errText));
        return false;
    }

    return true;
}

void MainWindow::updateCameraParameters(const QJsonObject &config)
{
    if (config.contains("MODE")) {
        hwStatus.amplifierMode = config["MODE"].toString() == "HC" ? HighCapacity : HighSensitivity;
    }
    if (config.contains("BIT")) {
        hwStatus.bitsPerPixel = config["BIT"].toString().toInt();
    }
    if (config.contains("GAIN")) {
        hwStatus.gainIndex = config["GAIN"].toString().toInt() - 1;
    }
    if (config.contains("BIN")) {
        hwStatus.binning = config["BIN"].toString().toInt();
    }

    LOG_GENERAL("Updated camera parameters:");
    LOG_GENERAL(QString("  Mode: %1").arg(hwStatus.amplifierMode == HighSensitivity ? "HS" : "HC"));
    LOG_GENERAL(QString("  Bits: %1").arg(hwStatus.bitsPerPixel));
    LOG_GENERAL(QString("  Gain: %1").arg(hwStatus.gainIndex + 1));
    LOG_GENERAL(QString("  Binning: %1x%1").arg(hwStatus.binning));

    sendCommandToMCU(QString("SET_PARAMS:%1,%2,%3,%4")
                         .arg(hwStatus.amplifierMode == HighSensitivity ? "HS" : "HC")
                         .arg(hwStatus.bitsPerPixel)
                         .arg(hwStatus.gainIndex + 1)
                         .arg(hwStatus.binning));
}

void MainWindow::updateCameraStatus()
{
    QString statusText;
    QString statusColor;

    switch (hwStatus.cameraState) {
    case Idle:
        statusText = "IDLE";
        statusColor = "green";
        break;
    case Waiting:
        statusText = "WAITING";
        statusColor = "blue";
        break;
    case Exposing:
        statusText = "EXPOSING";
        statusColor = "orange";
        break;
    case Reading:
        statusText = "READING";
        statusColor = "purple";
        break;
    case Downloading:
        statusText = "DOWNLOADING";
        statusColor = "cyan";
        break;
    case Error:
        statusText = "ERROR";
        statusColor = "red";
        break;
    }
    // 更新主板卡指示灯状态
    //updateMasterLedStatus();



    //ui->lblCameraStatus_2->setText(statusText);
    //ui->lblCameraStatus_2->setStyleSheet(
    // QString("background-color: %1; color: white; padding: 5px; border-radius: 5px;")
    //.arg(statusColor));
}

void MainWindow::sendResponse(const QString &response, QTcpSocket *client)
{
    try {
        if (!client)
        {
            qDebug()<<"[error] Client is null";
            LOG_CAMERA("attempted to send response to invalid client");
            return;
        }
        if (client->state() != QAbstractSocket::ConnectedState) {
            qDebug()<<"[Error] CLient not connected;";
            return;

        }

        client->write(response.toUtf8());
        qDebug()<<response;
        LOG_CAMERA("Sent response: " + response);


    } catch (const std::exception &e) {
        qDebug()<<"caught exception" << e.what();
    }

}

void MainWindow::onSetTemperature()
{
    double target = ui->spinTemperature_2->value();
    hwStatus.targetTemp = target;
    hwStatus.isCooling = true;

    sendCommandToMCU(QString("temp_set:%1").arg(target));

    LOG_GENERAL(QString("Temperature set to: %1 °C").arg(target));
}
